From 79b1ae8728db34a023565d35f3201579fc19f388 Mon Sep 17 00:00:00 2001 From: Michael Hirsch Date: Fri, 26 Aug 2016 13:26:37 +0200 Subject: [PATCH 01/43] introduce autocomplete textfield with RSQL suggestion Signed-off-by: Michael Hirsch --- .../repository/rsql/RsqlValidationOracle.java | 34 ++ .../hawkbit/repository/rsql/SuggestToken.java | 66 ++++ .../repository/rsql/SuggestionContext.java | 69 ++++ .../repository/rsql/SyntaxErrorContext.java | 56 ++++ .../rsql/ValidationOracleContext.java | 46 +++ .../RepositoryApplicationConfiguration.java | 8 + .../jpa/rsql/ParseExceptionWrapper.java | 187 +++++++++++ .../jpa/rsql/RsqlParserValidationOracle.java | 301 ++++++++++++++++++ .../rsql/RsqlParserValidationOracleTest.java | 81 +++++ .../eclipse/hawkbit/ui/AppWidgetSet.gwt.xml | 43 +-- .../AutoCompleteTextFieldComponent.java | 255 +++++++++++++++ .../CreateOrUpdateFilterHeader.java | 162 ++-------- .../FilterManagementView.java | 3 +- .../FilterQueryValidation.java | 182 ----------- .../filtermanagement/TargetFilterHeader.java | 1 + .../filtermanagement/TargetFilterTable.java | 1 - .../TextFieldSuggestionBox.gwt.xml | 17 + .../TextFieldSuggestionBox.java | 83 +++++ .../ui/filtermanagement/TokenDescription.java | 30 -- .../ui/filtermanagement/ValidationResult.java | 73 ----- .../AutoCompleteTextFieldConnector.java | 137 ++++++++ .../client/SuggestTokenDto.java | 68 ++++ .../client/SuggestionContextDto.java | 63 ++++ .../client/SuggestionsSelectList.java | 116 +++++++ .../TextFieldSuggestionBoxClientRpc.java | 32 ++ .../TextFieldSuggestionBoxServerRpc.java | 34 ++ .../customstyles/target-filter-query.scss | 47 ++- pom.xml | 2 +- 28 files changed, 1735 insertions(+), 462 deletions(-) create mode 100644 hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/rsql/RsqlValidationOracle.java create mode 100644 hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/rsql/SuggestToken.java create mode 100644 hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/rsql/SuggestionContext.java create mode 100644 hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/rsql/SyntaxErrorContext.java create mode 100644 hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/rsql/ValidationOracleContext.java create mode 100644 hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/ParseExceptionWrapper.java create mode 100644 hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlParserValidationOracle.java create mode 100644 hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlParserValidationOracleTest.java create mode 100644 hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/AutoCompleteTextFieldComponent.java delete mode 100644 hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/FilterQueryValidation.java create mode 100644 hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TextFieldSuggestionBox.gwt.xml create mode 100644 hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TextFieldSuggestionBox.java delete mode 100644 hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TokenDescription.java delete mode 100644 hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/ValidationResult.java create mode 100644 hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/AutoCompleteTextFieldConnector.java create mode 100644 hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/SuggestTokenDto.java create mode 100644 hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/SuggestionContextDto.java create mode 100644 hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/SuggestionsSelectList.java create mode 100644 hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/TextFieldSuggestionBoxClientRpc.java create mode 100644 hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/TextFieldSuggestionBoxServerRpc.java diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/rsql/RsqlValidationOracle.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/rsql/RsqlValidationOracle.java new file mode 100644 index 000000000..bb4b16817 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/rsql/RsqlValidationOracle.java @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.repository.rsql; + +/** + * An interface declaration which validates an RSQL based query syntax and + * allows providing suggestions e.g. in case of syntax errors or current cursor + * position. + */ +@FunctionalInterface +public interface RsqlValidationOracle { + + /** + * Parses and validates an given RSQL based query syntax and provides + * suggestion based on syntax error and cursor positioning. + * + * @param rsqlQuery + * an RSQL based query string to parse. + * @param cursorPosition + * the position of the cursor to retrieve suggestions at the + * position. {@code -1} indicates for no cursor suggestion + * @return a validation oracle context providing information about syntax + * errors and possible suggestions for fixing the syntax error or at + * the cursor position to replace tokens + */ + ValidationOracleContext suggest(final String rsqlQuery, final int cursorPosition); + +} diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/rsql/SuggestToken.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/rsql/SuggestToken.java new file mode 100644 index 000000000..55f009adf --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/rsql/SuggestToken.java @@ -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.rsql; + +/** + * A suggestion which contains the start and the end character position of the + * suggested token of the suggestion of the token and the actual suggestion. + */ +public class SuggestToken { + + private final int start; + private final int end; + private final String suggestion; + private final String tokenImageName; + + /** + * Constructor. + * + * @param start + * the character position of the start of the token + * @param end + * the character position of the end of the token + * @param the + * token image name which is currently parsed, e.g. entered image + * name is {@code na} which the suggestion can be based on and + * e.g. filter the suggestions. + * @param tokenImageName + * the entered name of the token, e.g. could be the beginning of + * the suggestion like 'na' or 'name' + * @param suggestion + * the token suggestion + */ + public SuggestToken(final int start, final int end, final String tokenImageName, final String suggestion) { + this.start = start; + this.end = end; + this.tokenImageName = tokenImageName; + this.suggestion = suggestion; + } + + public int getStart() { + return start; + } + + public int getEnd() { + return end; + } + + public String getSuggestion() { + return suggestion; + } + + public String getTokenImageName() { + return tokenImageName; + } + + @Override + public String toString() { + return "SuggestToken [start=" + start + ", end=" + end + ", suggestion=" + suggestion + "]"; + } +} diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/rsql/SuggestionContext.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/rsql/SuggestionContext.java new file mode 100644 index 000000000..2dd6531a6 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/rsql/SuggestionContext.java @@ -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.rsql; + +import java.util.ArrayList; +import java.util.List; + +/** + * The context which holds suggestions for the current cursor position. + */ +public class SuggestionContext { + + private String rsqlQuery; + private int cursorPosition; + private List suggestions = new ArrayList<>(); + + /** + * Default constructor. + */ + public SuggestionContext() { + // nothing to initialize + } + + /** + * Constructor. + * + * @param rsqlQuery + * the original RSQL based query the suggestions based on + * @param cursorPosition + * the current cursor position + * @param suggestions + * the suggestions for the current cursor position + */ + public SuggestionContext(final String rsqlQuery, final int cursorPosition, final List suggestions) { + this.rsqlQuery = rsqlQuery; + this.cursorPosition = cursorPosition; + this.suggestions = suggestions; + } + + public List getSuggestions() { + return suggestions; + } + + public int getCursorPosition() { + return cursorPosition; + } + + public String getRsqlQuery() { + return rsqlQuery; + } + + public void setRsqlQuery(final String rsqlQuery) { + this.rsqlQuery = rsqlQuery; + } + + public void setCursorPosition(final int cursorPosition) { + this.cursorPosition = cursorPosition; + } + + public void setSuggestions(final List suggestions) { + this.suggestions = suggestions; + } +} diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/rsql/SyntaxErrorContext.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/rsql/SyntaxErrorContext.java new file mode 100644 index 000000000..107ff755a --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/rsql/SyntaxErrorContext.java @@ -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.rsql; + +/** + * An syntax error context object which holds the character position of the + * syntax error and message. + */ +public class SyntaxErrorContext { + + private int characterPosition = -1; + private String errorMessage; + + /** + * Default constructor. + */ + public SyntaxErrorContext() { + // nothing to initialize + } + + /** + * Constructor. + * + * @param characterPosition + * the position of the character within the RSQL query string the + * error occurs. + * @param errorMessage + * the error message with further information + */ + public SyntaxErrorContext(final int characterPosition, final String errorMessage) { + this.characterPosition = characterPosition; + this.errorMessage = errorMessage; + } + + public int getCharacterPosition() { + return characterPosition; + } + + public void setCharacterPosition(final int characterPosition) { + this.characterPosition = characterPosition; + } + + public String getErrorMessage() { + return errorMessage; + } + + public void setErrorMessage(final String errorMessage) { + this.errorMessage = errorMessage; + } +} diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/rsql/ValidationOracleContext.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/rsql/ValidationOracleContext.java new file mode 100644 index 000000000..a6a71cf35 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/rsql/ValidationOracleContext.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.repository.rsql; + +/** + * A context object which contains information about validation and suggestions + * of a parsed RSQL query. + */ +public class ValidationOracleContext { + + private boolean syntaxError; + + private SuggestionContext suggestionContext; + + private SyntaxErrorContext syntaxErrorContext; + + public boolean isSyntaxError() { + return syntaxError; + } + + public SuggestionContext getSuggestionContext() { + return suggestionContext; + } + + public SyntaxErrorContext getSyntaxErrorContext() { + return syntaxErrorContext; + } + + public void setSyntaxError(final boolean syntaxError) { + this.syntaxError = syntaxError; + } + + public void setSuggestionContext(final SuggestionContext suggestionContext) { + this.suggestionContext = suggestionContext; + } + + public void setSyntaxErrorContext(final SyntaxErrorContext syntaxErrorContext) { + this.syntaxErrorContext = syntaxErrorContext; + } +} diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java index a62de9a06..c38612095 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java @@ -52,6 +52,8 @@ import org.eclipse.hawkbit.repository.jpa.model.helper.SystemManagementHolder; import org.eclipse.hawkbit.repository.jpa.model.helper.SystemSecurityContextHolder; import org.eclipse.hawkbit.repository.jpa.model.helper.TenantAwareHolder; import org.eclipse.hawkbit.repository.jpa.model.helper.TenantConfigurationManagementHolder; +import org.eclipse.hawkbit.repository.jpa.rsql.RsqlParserValidationOracle; +import org.eclipse.hawkbit.repository.rsql.RsqlValidationOracle; import org.eclipse.hawkbit.security.SecurityTokenGenerator; import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.tenancy.TenantAware; @@ -92,6 +94,12 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { @Autowired private EventBus eventBus; + @Bean + @ConditionalOnMissingBean + public RsqlValidationOracle rsqlValidationOracle() { + return new RsqlParserValidationOracle(); + } + /** * @return the {@link SystemSecurityContext} singleton bean which make it * accessible in beans which cannot access the service directly, diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/ParseExceptionWrapper.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/ParseExceptionWrapper.java new file mode 100644 index 000000000..08978eaaa --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/ParseExceptionWrapper.java @@ -0,0 +1,187 @@ +/** + * 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.jpa.rsql; + +import java.lang.reflect.Field; +import java.util.Arrays; + +import com.google.common.base.Throwables; + +import cz.jirutka.rsql.parser.ParseException; + +/** + * A {@link ParseException} wrapper which allows to access the parsing + * information from the exception using reflection due there is no other access + * of this information. See issue for requesting feature + * {@link https://github.com/jirutka/rsql-parser/issues/22} + */ +public class ParseExceptionWrapper { + + private static final String FIELD_EXPECTED_TOKEN_SEQ = "expectedTokenSequences"; + private static final String FIELD_CURRENT_TOKEN = "currentToken"; + + private final ParseException parseException; + private final Class parseExceptionClass; + private Field expectedTokenSequenceField; + private Field currentTokenField; + + /** + * Constructor. + * + * @param parseException + * the original parsing exception object to access its field + * using reflection + */ + public ParseExceptionWrapper(final ParseException parseException) { + this.parseException = parseException; + parseExceptionClass = parseException.getClass(); + + try { + expectedTokenSequenceField = getAccessibleField(parseExceptionClass, FIELD_EXPECTED_TOKEN_SEQ); + } catch (@SuppressWarnings("squid:S1166") final NoSuchFieldException e) { + expectedTokenSequenceField = null; + } + + try { + currentTokenField = getAccessibleField(parseExceptionClass, FIELD_CURRENT_TOKEN); + } catch (@SuppressWarnings("squid:S1166") final NoSuchFieldException e) { + currentTokenField = null; + } + } + + public int[][] getExpectedTokenSequence() { + if (expectedTokenSequenceField == null) { + return new int[0][0]; + } + return (int[][]) getValue(expectedTokenSequenceField, parseException); + } + + public TokenWrapper getCurrentToken() { + if (currentTokenField == null) { + return null; + } + return new TokenWrapper(getValue(currentTokenField, parseException)); + } + + @Override + public String toString() { + return "ParseExceptionWrapper [getExpectedTokenSequence()=" + Arrays.toString(getExpectedTokenSequence()) + + ", getCurrentToken()=" + getCurrentToken() + "]"; + } + + private static Field getAccessibleField(final Class clazz, final String field) throws NoSuchFieldException { + final Field declaredField = clazz.getDeclaredField(field); + declaredField.setAccessible(true); + return declaredField; + } + + private static Object getValue(final Field field, final Object instance) { + try { + return field.get(instance); + } catch (IllegalArgumentException | IllegalAccessException e) { + throw Throwables.propagate(e); + } + } + + /** + * A {@link TokenWrapper} which wraps the + * {@code cz.jirutka.rsql.parser.Token} class of the {@link ParseException} + * which otherwise is not accessible. + */ + public static final class TokenWrapper { + + private static final String FIELD_NEXT = "next"; + private static final String FIELD_KIND = "kind"; + private static final String FIELD_IMAGE = "image"; + private static final String FIELD_BEGIN_COL = "beginColumn"; + private static final String FIELD_END_COL = "endColumn"; + + private final Object tokenInstance; + + private Field nextTokenField; + private Field kindTokenField; + private Field imageTokenField; + private Field beginColumnTokenField; + private Field endColumnTokenField; + + private TokenWrapper(final Object tokenField) { + this.tokenInstance = tokenField; + + try { + nextTokenField = getAccessibleField(tokenField.getClass(), FIELD_NEXT); + } catch (@SuppressWarnings("squid:S1166") final NoSuchFieldException e) { + nextTokenField = null; + } + try { + kindTokenField = getAccessibleField(tokenField.getClass(), FIELD_KIND); + } catch (@SuppressWarnings("squid:S1166") final NoSuchFieldException e) { + kindTokenField = null; + } + + try { + imageTokenField = getAccessibleField(tokenField.getClass(), FIELD_IMAGE); + } catch (@SuppressWarnings("squid:S1166") final NoSuchFieldException e) { + imageTokenField = null; + } + + try { + beginColumnTokenField = getAccessibleField(tokenField.getClass(), FIELD_BEGIN_COL); + } catch (@SuppressWarnings("squid:S1166") final NoSuchFieldException e) { + beginColumnTokenField = null; + } + + try { + endColumnTokenField = getAccessibleField(tokenField.getClass(), FIELD_END_COL); + } catch (@SuppressWarnings("squid:S1166") final NoSuchFieldException e) { + endColumnTokenField = null; + } + } + + public TokenWrapper getNext() { + final Object nextToken = getValue(nextTokenField, tokenInstance); + return nextToken != null ? new TokenWrapper(nextToken) : null; + + } + + public int getKind() { + if (kindTokenField == null) { + return 0; + } + return (int) getValue(kindTokenField, tokenInstance); + } + + public String getImage() { + if (imageTokenField == null) { + return null; + } + return (String) getValue(imageTokenField, tokenInstance); + } + + public int getBeginColumn() { + if (beginColumnTokenField == null) { + return 0; + } + return (int) getValue(beginColumnTokenField, tokenInstance); + } + + public int getEndColumn() { + if (endColumnTokenField == null) { + return 0; + } + return (int) getValue(endColumnTokenField, tokenInstance); + } + + @Override + public String toString() { + return "TokenWrapper [tokenInstance=" + tokenInstance + ", getNext()=" + getNext() + ", getKind()=" + + getKind() + ", getImage()=" + getImage() + ", getBeginColumn()=" + getBeginColumn() + + ", getEndColumn()=" + getEndColumn() + "]"; + } + } +} diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlParserValidationOracle.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlParserValidationOracle.java new file mode 100644 index 000000000..8eb21400f --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlParserValidationOracle.java @@ -0,0 +1,301 @@ +package org.eclipse.hawkbit.repository.jpa.rsql; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import org.eclipse.hawkbit.repository.TargetFields; +import org.eclipse.hawkbit.repository.TargetManagement; +import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException; +import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException; +import org.eclipse.hawkbit.repository.jpa.rsql.ParseExceptionWrapper.TokenWrapper; +import org.eclipse.hawkbit.repository.rsql.RsqlValidationOracle; +import org.eclipse.hawkbit.repository.rsql.SuggestToken; +import org.eclipse.hawkbit.repository.rsql.SuggestionContext; +import org.eclipse.hawkbit.repository.rsql.SyntaxErrorContext; +import org.eclipse.hawkbit.repository.rsql.ValidationOracleContext; +import org.eclipse.persistence.exceptions.ConversionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.orm.jpa.JpaSystemException; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; + +import cz.jirutka.rsql.parser.ParseException; +import cz.jirutka.rsql.parser.RSQLParserException; + +/** + * An implementation of {@link RsqlValidationOracle} which retrieves the + * exception using the {@link ParseException} to retrieve the suggestions. + * + * The suggestion only works when there are syntax errors existing because the + * information about current and next tokens in the RSQL syntax are from the + * {@link ParseException}. + * + * There is a feature request on the GitHub project + * {@link https://github.com/jirutka/rsql-parser/issues/22}. + */ +public class RsqlParserValidationOracle implements RsqlValidationOracle { + + private static final Logger LOGGER = LoggerFactory.getLogger(RsqlParserValidationOracle.class); + + @Autowired + private TargetManagement targetManagement; + + @Override + public ValidationOracleContext suggest(final String rsqlQuery, final int cursorPosition) { + + final List expectedTokens = new ArrayList<>(); + final ValidationOracleContext context = new ValidationOracleContext(); + context.setSyntaxError(true); + final SuggestionContext suggestionContext = new SuggestionContext(); + context.setSuggestionContext(suggestionContext); + final SyntaxErrorContext errorContext = new SyntaxErrorContext(); + context.setSyntaxErrorContext(errorContext); + + try { + targetManagement.findTargetsAll(rsqlQuery, new PageRequest(0, 1)); + context.setSyntaxError(false); + suggestionContext.getSuggestions().addAll(getLogicalOperatorSuggestion(rsqlQuery)); + } catch (final RSQLParameterSyntaxException | RSQLParserException ex) { + setExceptionDetails(new Exception(ex.getCause().getCause()), expectedTokens); + errorContext.setErrorMessage(getCustomMessage(ex.getCause().getMessage(), expectedTokens)); + suggestionContext.setSuggestions(expectedTokens); + LOGGER.trace("Syntax exception on parsing :", ex); + } catch (final RSQLParameterUnsupportedFieldException | IllegalArgumentException ex) { + errorContext.setErrorMessage(getCustomMessage(ex.getMessage(), null)); + LOGGER.trace("Illegal argument on parsing :", ex); + } catch (@SuppressWarnings("squid:S1166") final ConversionException | JpaSystemException e) { + // noop + } + return context; + } + + private static Collection getLogicalOperatorSuggestion(final String rsqlQuery) { + final int currentQueryLength = rsqlQuery.length(); + // only return and/or suggestion when there is a space at the end + if (rsqlQuery.endsWith(" ")) { + final Collection tokenImages = TokenDescription.getTokenImage(TokenDescription.LOGICAL_OP); + final List logicalOps = new ArrayList<>(tokenImages.size()); + for (final String tokenImage : tokenImages) { + logicalOps.add(new SuggestToken(currentQueryLength, currentQueryLength + tokenImage.length(), null, + tokenImage)); + } + return logicalOps; + } + return Collections.emptyList(); + } + + private static void setExceptionDetails(final Exception ex, final List expectedTokens) { + expectedTokens.addAll(getNextTokens(ex)); + } + + private static List getNextTokens(final Exception e) { + final ParseException parseException = findParseException(e); + if (parseException != null) { + final List listTokens = new ArrayList<>(); + final ParseExceptionWrapper parseExceptionWrapper = new ParseExceptionWrapper(parseException); + final int[][] expectedTokenSequence = parseExceptionWrapper.getExpectedTokenSequence(); + final TokenWrapper currentToken = parseExceptionWrapper.getCurrentToken(); + final TokenWrapper nextToken = currentToken.getNext(); + final int currentTokenKind = currentToken.getKind(); + final String currentTokenImageName = currentToken.getImage(); + final int nextTokenBeginColumn = nextToken.getBeginColumn(); + final int currentTokenEndColumn = currentToken.getEndColumn(); + + // token == 5 is the field token, reverse engineering. + if (currentTokenKind == 5) { + final Optional> handleFieldTokenSuggestion = handleFieldTokenSuggestion( + currentTokenImageName, nextTokenBeginColumn, currentTokenEndColumn); + if (handleFieldTokenSuggestion.isPresent()) { + return handleFieldTokenSuggestion.get(); + } + } + + for (final int[] is : expectedTokenSequence) { + for (final int i : is) { + final Collection tokenImage = TokenDescription.getTokenImage(i); + if (tokenImage != null && !tokenImage.isEmpty()) { + tokenImage.forEach(image -> listTokens.add(new SuggestToken(currentTokenEndColumn + 1, + nextTokenBeginColumn + image.length(), null, image))); + } + } + } + return listTokens; + } + return Collections.emptyList(); + + } + + private static Optional> handleFieldTokenSuggestion(final String currentTokenImageName, + final int nextTokenBeginColumn, final int currentTokenEndColumn) { + final boolean containsDot = currentTokenImageName.indexOf('.') != -1; + if (shouldSuggestTopLevelFieldNames(currentTokenImageName, containsDot)) { + return Optional + .of(FieldNameDescription.toTopSuggestToken(nextTokenBeginColumn - currentTokenImageName.length(), + nextTokenBeginColumn + currentTokenImageName.length(), currentTokenImageName)); + } else if (shouldSuggestDotToken(currentTokenImageName, containsDot)) { + return Optional.of( + Lists.newArrayList(new SuggestToken(currentTokenEndColumn, nextTokenBeginColumn + 1, null, "."))); + } else if (shouldSuggestSubTokenFieldNames(currentTokenImageName, containsDot)) { + return handleSubtokenSuggestion(currentTokenImageName, nextTokenBeginColumn); + } + return Optional.empty(); + } + + private static boolean shouldSuggestSubTokenFieldNames(final String currentTokenImageName, + final boolean containsDot) { + return containsDot && !FieldNameDescription.containsValue(currentTokenImageName); + } + + private static boolean shouldSuggestDotToken(final String currentTokenImageName, final boolean containsDot) { + return !containsDot && FieldNameDescription.hasSubEntries(currentTokenImageName); + } + + private static boolean shouldSuggestTopLevelFieldNames(final String currentTokenImageName, + final boolean containsDot) { + return !containsDot && !FieldNameDescription.containsValue(currentTokenImageName) + && !FieldNameDescription.hasSubEntries(currentTokenImageName); + } + + private static Optional> handleSubtokenSuggestion(final String currentTokenImageName, + final int nextTokenBeginColumn) { + final String[] split = currentTokenImageName.split("\\."); + for (final String string : split) { + if (FieldNameDescription.containsValue(string)) { + final String subTokenImage = split.length > 1 ? split[1] : null; + final int subTokenBegin = nextTokenBeginColumn + currentTokenImageName.indexOf('.') + 1; + return Optional.of(FieldNameDescription.toSubSuggestToken(subTokenBegin, subTokenBegin + 1, string, + subTokenImage)); + } + } + return Optional.empty(); + } + + private static ParseException findParseException(final Throwable e) { + if (e != null && e instanceof ParseException) { + return (ParseException) e; + } else if (e != null && e.getCause() != null) { + return findParseException(e.getCause()); + } + return null; + } + + private static String getCustomMessage(final String message, final List expectedTokens) { + String builder = message; + if (message.contains(":")) { + builder = message.substring(message.indexOf(':') + 1, message.length()); + if (builder.indexOf("Was expecting") != -1) { + builder = builder.substring(0, builder.lastIndexOf("Was expecting")); + } + if (null != expectedTokens && !expectedTokens.isEmpty()) { + final StringBuilder tokens = new StringBuilder(); + expectedTokens.stream().forEach(value -> tokens.append(value.getSuggestion() + ",")); + builder = builder.concat("Was expecting :" + tokens.toString().substring(0, tokens.length() - 1)); + } + builder = builder.replace('\r', ' '); + builder = builder.replace('\n', ' '); + builder = builder.replaceAll(">", " "); + builder = builder.replaceAll("<", " "); + } + return builder; + } + + private static final class TokenDescription { + + private static final Multimap TOKEN_MAP = ArrayListMultimap.create(); + + private static final int LOGICAL_OP = 8; + private static final int COMPARATOR = 12; + + static { + TOKEN_MAP.put(LOGICAL_OP, "and"); + TOKEN_MAP.put(LOGICAL_OP, "or"); + TOKEN_MAP.put(COMPARATOR, "=="); + TOKEN_MAP.put(COMPARATOR, "!="); + TOKEN_MAP.put(COMPARATOR, "=ge="); + TOKEN_MAP.put(COMPARATOR, "=le="); + TOKEN_MAP.put(COMPARATOR, "=gt="); + TOKEN_MAP.put(COMPARATOR, "=lt="); + TOKEN_MAP.put(COMPARATOR, "=in="); + TOKEN_MAP.put(COMPARATOR, "=out="); + } + + private TokenDescription() { + + } + + private static Collection getTokenImage(final int tokenIndex) { + return TOKEN_MAP.get(tokenIndex); + } + + } + + private static final class FieldNameDescription { + + private static final Set FIELD_NAMES = Arrays.stream(TargetFields.values()) + .map(field -> field.toString().toLowerCase()).collect(Collectors.toSet()); + + private static final Map> SUB_NAMES = Arrays.stream(TargetFields.values()).collect( + Collectors.toMap(field -> field.toString().toLowerCase(), field -> field.getSubEntityAttributes())); + + private FieldNameDescription() { + + } + + private static boolean hasSubEntries(final String tokenImageName) { + final String tmpTokenName; + if (tokenImageName.contains(".")) { + final String[] split = tokenImageName.split("\\."); + if (split.length > 0) { + tmpTokenName = split[0]; + } else { + return false; + } + } else { + tmpTokenName = tokenImageName; + } + return Arrays.stream(TargetFields.values()).filter(field -> field.toString().equalsIgnoreCase(tmpTokenName)) + .map(field -> field.getSubEntityAttributes()).flatMap(subentities -> subentities.stream()) + .count() > 0; + } + + private static List toTopSuggestToken(final int beginToken, final int endToken, + final String tokenImageName) { + return FIELD_NAMES.stream() + .map(field -> new SuggestToken(beginToken, endToken, tokenImageName, field.toLowerCase())) + .collect(Collectors.toList()); + } + + private static List toSubSuggestToken(final int beginToken, final int endToken, + final String topToken, final String tokenImageName) { + return Arrays.stream(TargetFields.values()).filter(field -> field.toString().equalsIgnoreCase(topToken)) + .map(field -> field.getSubEntityAttributes()).flatMap(list -> list.stream()) + .map(subentity -> new SuggestToken(beginToken, endToken, tokenImageName, subentity)) + .collect(Collectors.toList()); + } + + private static boolean containsValue(final String imageName) { + if (imageName.contains(".")) { + final String[] split = imageName.split("\\."); + if (split.length > 1 && FIELD_NAMES.contains(split[0].toLowerCase())) { + return SUB_NAMES.get(split[0].toLowerCase()).stream() + .filter(subname -> new String(split[0] + "." + subname).equalsIgnoreCase(imageName)) + .count() > 0; + } + } + return FIELD_NAMES.stream().filter(value -> value.equalsIgnoreCase(imageName)).count() > 0; + } + } + +} diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlParserValidationOracleTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlParserValidationOracleTest.java new file mode 100644 index 000000000..c1b43eecd --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlParserValidationOracleTest.java @@ -0,0 +1,81 @@ +/** + * 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.jpa.rsql; + +import static org.fest.assertions.api.Assertions.assertThat; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.eclipse.hawkbit.repository.TargetFields; +import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest; +import org.eclipse.hawkbit.repository.rsql.RsqlValidationOracle; +import org.eclipse.hawkbit.repository.rsql.ValidationOracleContext; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import ru.yandex.qatools.allure.annotations.Description; +import ru.yandex.qatools.allure.annotations.Features; +import ru.yandex.qatools.allure.annotations.Stories; + +@Features("Component Tests - Repository") +@Stories("RSQL filter suggestion") +public class RsqlParserValidationOracleTest extends AbstractJpaIntegrationTest { + + @Autowired + private RsqlValidationOracle rsqlValidationOracle; + + private static final String[] OP_SUGGESTIONS = new String[] { "==", "!=", "=ge=", "=le=", "=gt=", "=lt=", "=in=", + "=out=" }; + private static final String[] FIELD_SUGGESTIONS = Arrays.stream(TargetFields.values()) + .map(field -> field.name().toLowerCase()).toArray(size -> new String[size]); + private static final String[] AND_OR_SUGGESTIONS = new String[] { "and", "or" }; + private static final String[] NAME_VERSION_SUGGESTIONS = new String[] { "name", "version" }; + + @Test + @Description("Verifies that suggestions contains all possible field names") + public void suggestionContainsAllFieldNames() { + final String rsqlQuery = "na"; + final List currentSuggestions = getSuggestions(rsqlQuery); + assertThat(currentSuggestions).containsOnly(FIELD_SUGGESTIONS); + } + + @Test + @Description("Verifies that suggestions only contains the allowed operators") + public void suggestionContainsOnlyOperators() { + final String rsqlQuery = "name"; + final List currentSuggestions = getSuggestions(rsqlQuery); + assertThat(currentSuggestions).containsOnly(OP_SUGGESTIONS); + } + + @Test + @Description("Verifies that suggestions only contains operator to combine RSQL filters (and, or)") + public void suggestionContainsOnlyAndOrOperator() { + final String rsqlQuery = "name==a "; + final List currentSuggestions = getSuggestions(rsqlQuery); + assertThat(currentSuggestions).containsOnly(AND_OR_SUGGESTIONS); + } + + @Test + @Description("Verifies that sub suggestions are shown") + public void suggestionContainsSubFieldSuggestions() { + final String rsqlQuery = "assignedds."; + final List currentSuggestions = getSuggestions(rsqlQuery); + assertThat(currentSuggestions).containsOnly(NAME_VERSION_SUGGESTIONS); + } + + private List getSuggestions(final String rsqlQuery) { + final ValidationOracleContext suggest = rsqlValidationOracle.suggest(rsqlQuery, -1); + final List currentSuggestions = suggest.getSuggestionContext().getSuggestions().stream() + .map(suggestion -> suggestion.getSuggestion()).collect(Collectors.toList()); + return currentSuggestions; + } + +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/AppWidgetSet.gwt.xml b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/AppWidgetSet.gwt.xml index c6ee9b5b5..2141eb6b1 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/AppWidgetSet.gwt.xml +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/AppWidgetSet.gwt.xml @@ -1,44 +1,33 @@ - + - + - - + + - + - + - + - + - + - + - + + - + + - + diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/AutoCompleteTextFieldComponent.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/AutoCompleteTextFieldComponent.java new file mode 100644 index 000000000..e3d766391 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/AutoCompleteTextFieldComponent.java @@ -0,0 +1,255 @@ +/** + * 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.ui.filtermanagement; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.Executor; + +import javax.annotation.PostConstruct; + +import org.eclipse.hawkbit.repository.rsql.RsqlValidationOracle; +import org.eclipse.hawkbit.ui.common.builder.TextFieldBuilder; +import org.eclipse.hawkbit.ui.filtermanagement.event.CustomFilterUIEvent; +import org.eclipse.hawkbit.ui.filtermanagement.state.FilterManagementUIState; +import org.eclipse.hawkbit.ui.utils.SPUIComponentIdProvider; +import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions; +import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.vaadin.spring.events.EventBus; + +import com.vaadin.event.ShortcutListener; +import com.vaadin.server.FontAwesome; +import com.vaadin.shared.ui.label.ContentMode; +import com.vaadin.spring.annotation.SpringComponent; +import com.vaadin.spring.annotation.ViewScope; +import com.vaadin.ui.AbstractTextField.TextChangeEventMode; +import com.vaadin.ui.Alignment; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.Label; +import com.vaadin.ui.TextField; +import com.vaadin.ui.UI; + +/** + * An textfield with the {@link TextFieldSuggestionBox} extension which shows + * suggestions in a suggestion-pop-up window while typing. + */ +@SpringComponent +@ViewScope +public class AutoCompleteTextFieldComponent extends HorizontalLayout { + + private static final long serialVersionUID = 1L; + + @Autowired + private FilterManagementUIState filterManagementUIState; + + @Autowired + private transient EventBus.SessionEventBus eventBus; + + @Autowired + private transient RsqlValidationOracle rsqlValidationOracle; + + @Autowired + @Qualifier("uiExecutor") + private transient Executor executor; + + private transient List listeners = new LinkedList<>(); + + private final Label validationIcon; + private final TextField queryTextField; + + /** + * Constructor. + */ + public AutoCompleteTextFieldComponent() { + + queryTextField = createSearchField(); + validationIcon = createStatusIcon(); + + setSizeUndefined(); + setSpacing(true); + addStyleName("custom-search-layout"); + addComponents(validationIcon, queryTextField); + setComponentAlignment(validationIcon, Alignment.TOP_CENTER); + } + + /** + * Called by the spring-framework when this bean has be post-constructed. + */ + @PostConstruct + public void postConstruct() { + new TextFieldSuggestionBox(rsqlValidationOracle, this).extend(queryTextField); + } + + /** + * Clears the textfield and resets the validation icon. + */ + public void clear() { + queryTextField.clear(); + validationIcon.setValue(FontAwesome.CHECK_CIRCLE.getHtml()); + validationIcon.setStyleName("hide-status-label"); + } + + @Override + public void focus() { + queryTextField.focus(); + } + + /** + * Adds the given listener + * + * @param textChangeListener + * the listener to be called in case of text changed + */ + public void addTextChangeListener(final FilterQueryChangeListener textChangeListener) { + listeners.add(textChangeListener); + } + + public void setValue(final String textValue) { + queryTextField.setValue(textValue); + } + + public String getValue() { + return queryTextField.getValue(); + } + + /** + * Called when the filter-query has been changed in the textfield, e.g. from + * client-side. + * + * @param currentText + * the current text of the textfield which has been changed + * @param valid + * {@code boolean} if the current text is RSQL syntax valid + * otherwise {@code false} + * @param validationMessage + * a message shown in case of syntax errors as tooltip + */ + public void onQueryFilterChange(final String currentText, final boolean valid, final String validationMessage) { + if (valid) { + showValidationSuccesIcon(currentText); + } else { + showValidationFailureIcon(validationMessage); + } + listeners.forEach(listener -> listener.queryChanged(valid, currentText)); + } + + /** + * Shows the validation success icon in the textfield + * + * @param text + * the text to store in the UI state object + */ + public void showValidationSuccesIcon(final String text) { + validationIcon.setValue(FontAwesome.CHECK_CIRCLE.getHtml()); + validationIcon.setStyleName(SPUIStyleDefinitions.SUCCESS_ICON); + filterManagementUIState.setFilterQueryValue(text); + filterManagementUIState.setIsFilterByInvalidFilterQuery(Boolean.FALSE); + } + + /** + * Shows the validation error icon in the textfield + * + * @param validationMessage + * the validation message which should be added to the error-icon + * tooltip + */ + public void showValidationFailureIcon(final String validationMessage) { + validationIcon.setValue(FontAwesome.TIMES_CIRCLE.getHtml()); + validationIcon.setStyleName(SPUIStyleDefinitions.ERROR_ICON); + validationIcon.setDescription(validationMessage); + filterManagementUIState.setFilterQueryValue(null); + filterManagementUIState.setIsFilterByInvalidFilterQuery(Boolean.TRUE); + } + + public boolean isValidationError() { + return validationIcon.getStyleName().equals(SPUIStyleDefinitions.ERROR_ICON); + } + + private TextField createSearchField() { + final TextField textField = new TextFieldBuilder().immediate(true).id("custom.query.text.Id") + .maxLengthAllowed(SPUILabelDefinitions.TARGET_FILTER_QUERY_TEXT_FIELD_LENGTH).buildTextComponent(); + textField.addStyleName("target-filter-textfield"); + textField.setWidth(900.0F, Unit.PIXELS); + textField.setTextChangeEventMode(TextChangeEventMode.EAGER); + textField.setImmediate(true); + textField.setTextChangeTimeout(100); + textField.addShortcutListener(new EnterShortCutListener()); + return textField; + } + + private static Label createStatusIcon() { + final Label statusIcon = new Label(); + statusIcon.setImmediate(true); + statusIcon.setContentMode(ContentMode.HTML); + statusIcon.setSizeFull(); + setInitialStatusIconStyle(statusIcon); + statusIcon.setId(SPUIComponentIdProvider.VALIDATION_STATUS_ICON_ID); + return statusIcon; + } + + private static void setInitialStatusIconStyle(final Label statusIcon) { + statusIcon.setValue(FontAwesome.CHECK_CIRCLE.getHtml()); + statusIcon.setStyleName("hide-status-label"); + } + + class StatusCircledAsync implements Runnable { + private final UI current; + + public StatusCircledAsync(final UI current) { + this.current = current; + } + + @Override + public void run() { + UI.setCurrent(current); + eventBus.publish(this, CustomFilterUIEvent.FILTER_TARGET_BY_QUERY); + } + } + + private final class EnterShortCutListener extends ShortcutListener { + + private static final long serialVersionUID = 1L; + + public EnterShortCutListener() { + super("Enter", KeyCode.ENTER, new int[0]); + } + + @Override + public void handleAction(final Object sender, final Object target) { + if (!isValidationError()) { + showValidationInProgress(); + executor.execute(new StatusCircledAsync(UI.getCurrent())); + } + } + + private void showValidationInProgress() { + validationIcon.setValue(null); + validationIcon.addStyleName("show-status-label"); + validationIcon.setStyleName(SPUIStyleDefinitions.TARGET_FILTER_SEARCH_PROGRESS_INDICATOR_STYLE); + } + } + + /** + * Change listener on the textfield. + */ + @FunctionalInterface + public interface FilterQueryChangeListener { + /** + * Called when the text has been changed and validated. + * + * @param valid + * indicates if the entered query text is valid + * @param query + * the entered query text + */ + void queryChanged(final boolean valid, final String query); + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterHeader.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterHeader.java index b6f186188..34607da2b 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterHeader.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterHeader.java @@ -8,7 +8,7 @@ */ package org.eclipse.hawkbit.ui.filtermanagement; -import java.util.concurrent.Executor; +import java.util.Optional; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @@ -23,6 +23,7 @@ import org.eclipse.hawkbit.ui.common.builder.TextFieldBuilder; import org.eclipse.hawkbit.ui.components.SPUIButton; import org.eclipse.hawkbit.ui.components.SPUIComponentProvider; import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleSmallNoBorder; +import org.eclipse.hawkbit.ui.filtermanagement.AutoCompleteTextFieldComponent.FilterQueryChangeListener; import org.eclipse.hawkbit.ui.filtermanagement.event.CustomFilterUIEvent; import org.eclipse.hawkbit.ui.filtermanagement.state.FilterManagementUIState; import org.eclipse.hawkbit.ui.utils.I18N; @@ -31,7 +32,6 @@ import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions; import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions; import org.eclipse.hawkbit.ui.utils.UINotification; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.vaadin.spring.events.EventBus; import org.vaadin.spring.events.EventScope; import org.vaadin.spring.events.annotation.EventBusListenerMethod; @@ -40,16 +40,11 @@ import com.google.common.base.Strings; import com.vaadin.event.FieldEvents.BlurEvent; import com.vaadin.event.FieldEvents.BlurListener; import com.vaadin.event.FieldEvents.TextChangeEvent; -import com.vaadin.event.FieldEvents.TextChangeListener; import com.vaadin.event.LayoutEvents.LayoutClickEvent; import com.vaadin.event.LayoutEvents.LayoutClickListener; -import com.vaadin.event.ShortcutAction.KeyCode; import com.vaadin.server.FontAwesome; -import com.vaadin.shared.ui.label.ContentMode; import com.vaadin.spring.annotation.SpringComponent; import com.vaadin.spring.annotation.ViewScope; -import com.vaadin.ui.AbstractField; -import com.vaadin.ui.AbstractTextField.TextChangeEventMode; import com.vaadin.ui.Alignment; import com.vaadin.ui.Button; import com.vaadin.ui.Button.ClickEvent; @@ -57,7 +52,6 @@ import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.Label; import com.vaadin.ui.Link; import com.vaadin.ui.TextField; -import com.vaadin.ui.UI; import com.vaadin.ui.VerticalLayout; import com.vaadin.ui.themes.ValoTheme; @@ -94,11 +88,10 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button private transient UiProperties uiProperties; @Autowired - @Qualifier("uiExecutor") - private transient Executor executor; + private transient EntityFactory entityFactory; @Autowired - private transient EntityFactory entityFactory; + private AutoCompleteTextFieldComponent queryTextField; private HorizontalLayout breadcrumbLayout; @@ -108,8 +101,6 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button private Label headerCaption; - private TextField queryTextField; - private TextField nameTextField; private Label nameLabel; @@ -120,10 +111,6 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button private Link helpLink; - private Label validationIcon; - - private HorizontalLayout searchLayout; - private String oldFilterName; private String oldFilterQuery; @@ -136,8 +123,6 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button private LayoutClickListener nameLayoutClickListner; - private boolean validationFailed = false; - /** * Initialize the Campaign Status History Header. */ @@ -170,8 +155,6 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button } else if (custFUIEvent == CustomFilterUIEvent.CREATE_NEW_FILTER_CLICK) { setUpCaptionLayout(true); resetComponents(); - } else if (custFUIEvent == CustomFilterUIEvent.UPDATE_TARGET_FILTER_SEARCH_ICON) { - UI.getCurrent().access(() -> updateStatusIconAfterTablePopulated()); } } @@ -183,38 +166,22 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button oldFilterQuery = filterManagementUIState.getTfQuery().get().getQuery(); } breadcrumbName.setValue(nameLabel.getValue()); - showValidationSuccesIcon(); + queryTextField.showValidationSuccesIcon(filterManagementUIState.getFilterQueryValue()); titleFilterIconsLayout.addStyleName(SPUIStyleDefinitions.TARGET_FILTER_CAPTION_LAYOUT); headerCaption.setVisible(false); setUpCaptionLayout(false); } private void resetComponents() { + queryTextField.clear(); + queryTextField.focus(); headerCaption.setVisible(true); breadcrumbName.setValue(headerCaption.getValue()); nameLabel.setValue(""); - queryTextField.setValue(""); - setInitialStatusIconStyle(validationIcon); - validationFailed = false; saveButton.setEnabled(false); titleFilterIconsLayout.removeStyleName(SPUIStyleDefinitions.TARGET_FILTER_CAPTION_LAYOUT); } - private Label createStatusIcon() { - final Label statusIcon = new Label(); - statusIcon.setImmediate(true); - statusIcon.setContentMode(ContentMode.HTML); - statusIcon.setSizeFull(); - setInitialStatusIconStyle(statusIcon); - statusIcon.setId(SPUIComponentIdProvider.VALIDATION_STATUS_ICON_ID); - return statusIcon; - } - - private void setInitialStatusIconStyle(final Label statusIcon) { - statusIcon.setValue(FontAwesome.CHECK_CIRCLE.getHtml()); - statusIcon.setStyleName("hide-status-label"); - } - private void createComponents() { breadcrumbButton = createBreadcrumbButton(); @@ -227,10 +194,6 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button nameTextField = createNameTextField(); nameTextField.setWidth(380, Unit.PIXELS); - queryTextField = createSearchField(); - addSearchLisenter(); - - validationIcon = createStatusIcon(); saveButton = createSaveButton(); helpLink = SPUIComponentProvider.getHelpLink(uiProperties.getLinks().getDocumentation().getTargetfilterView()); @@ -282,6 +245,14 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button } } }; + + queryTextField.addTextChangeListener(new FilterQueryChangeListener() { + @Override + public void queryChanged(final boolean valid, final String query) { + enableDisableSaveButton(!valid, query); + } + }); + } private void onFilterNameChange(final TextChangeEvent event) { @@ -318,15 +289,6 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button titleFilterLayout.setComponentAlignment(titleFilterIconsLayout, Alignment.TOP_LEFT); titleFilterLayout.setComponentAlignment(closeIcon, Alignment.TOP_RIGHT); - validationIcon = createStatusIcon(); - - searchLayout = new HorizontalLayout(); - searchLayout.setSizeUndefined(); - searchLayout.setSpacing(false); - searchLayout.addComponents(validationIcon, queryTextField); - searchLayout.addStyleName("custom-search-layout"); - searchLayout.setComponentAlignment(validationIcon, Alignment.TOP_CENTER); - final HorizontalLayout iconLayout = new HorizontalLayout(); iconLayout.setSizeUndefined(); iconLayout.setSpacing(false); @@ -335,7 +297,7 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button final HorizontalLayout queryLayout = new HorizontalLayout(); queryLayout.setSizeUndefined(); queryLayout.setSpacing(true); - queryLayout.addComponents(searchLayout, iconLayout); + queryLayout.addComponents(queryTextField, iconLayout); addComponent(breadcrumbLayout); addComponent(titleFilterLayout); @@ -358,58 +320,6 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button } } - private void addSearchLisenter() { - queryTextField.addTextChangeListener(new TextChangeListener() { - private static final long serialVersionUID = -6668604418942689391L; - - @Override - public void textChange(final TextChangeEvent event) { - validationIcon.addStyleName("show-status-label"); - showValidationInProgress(); - onQueryChange(event.getText()); - executor.execute(new StatusCircledAsync(UI.getCurrent())); - } - - }); - } - - class StatusCircledAsync implements Runnable { - private final UI current; - - public StatusCircledAsync(final UI current) { - this.current = current; - } - - @Override - public void run() { - UI.setCurrent(current); - eventBus.publish(this, CustomFilterUIEvent.FILTER_TARGET_BY_QUERY); - } - } - - private void onQueryChange(final String input) { - if (!Strings.isNullOrEmpty(input)) { - final ValidationResult validationResult = FilterQueryValidation.getExpectedTokens(input); - if (!validationResult.getIsValidationFailed()) { - filterManagementUIState.setFilterQueryValue(input); - filterManagementUIState.setIsFilterByInvalidFilterQuery(Boolean.FALSE); - validationFailed = false; - } else { - validationFailed = true; - filterManagementUIState.setFilterQueryValue(null); - filterManagementUIState.setIsFilterByInvalidFilterQuery(Boolean.TRUE); - validationIcon.setDescription(validationResult.getMessage()); - showValidationFailureIcon(); - } - enableDisableSaveButton(validationFailed, input); - } else { - setInitialStatusIconStyle(validationIcon); - filterManagementUIState.setFilterQueryValue(null); - filterManagementUIState.setIsFilterByInvalidFilterQuery(Boolean.TRUE); - } - queryTextField.setValue(input); - } - private void enableDisableSaveButton(final boolean validationFailed, final String query) { if (validationFailed || (isNameAndQueryEmpty(nameTextField.getValue(), query) || (query.equals(oldFilterQuery) && nameTextField.getValue().equals(oldFilterName)))) { @@ -428,21 +338,6 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button return false; } - private void showValidationSuccesIcon() { - validationIcon.setValue(FontAwesome.CHECK_CIRCLE.getHtml()); - validationIcon.setStyleName(SPUIStyleDefinitions.SUCCESS_ICON); - } - - private void showValidationFailureIcon() { - validationIcon.setValue(FontAwesome.TIMES_CIRCLE.getHtml()); - validationIcon.setStyleName(SPUIStyleDefinitions.ERROR_ICON); - } - - private void showValidationInProgress() { - validationIcon.setValue(null); - validationIcon.setStyleName(SPUIStyleDefinitions.TARGET_FILTER_SEARCH_PROGRESS_INDICATOR_STYLE); - } - private SPUIButton createSearchResetIcon() { final SPUIButton button = (SPUIButton) SPUIComponentProvider.getButton("create.custom.filter.close.Id", "", "", null, false, FontAwesome.TIMES, SPUIButtonStyleSmallNoBorder.class); @@ -450,18 +345,6 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button return button; } - private static TextField createSearchField() { - final TextField textField = new TextFieldBuilder().immediate(true).id("custom.query.text.Id") - .maxLengthAllowed(SPUILabelDefinitions.TARGET_FILTER_QUERY_TEXT_FIELD_LENGTH).buildTextComponent(); - textField.addStyleName("target-filter-textfield"); - textField.setWidth(900.0F, Unit.PIXELS); - textField.setTextChangeEventMode(TextChangeEventMode.LAZY); - textField.setTextChangeTimeout(1000); - - textField.addShortcutListener(new AbstractField.FocusShortcut(textField, KeyCode.ENTER)); - return textField; - } - private void closeFilterLayout() { filterManagementUIState.setFilterQueryValue(null); filterManagementUIState.setCreateFilterBtnClicked(false); @@ -508,7 +391,11 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button } private void updateCustomFilter() { - final TargetFilterQuery targetFilterQuery = filterManagementUIState.getTfQuery().get(); + final Optional tfQuery = filterManagementUIState.getTfQuery(); + if (!tfQuery.isPresent()) { + return; + } + final TargetFilterQuery targetFilterQuery = tfQuery.get(); targetFilterQuery.setName(nameTextField.getValue()); targetFilterQuery.setQuery(queryTextField.getValue()); final TargetFilterQuery updatedTargetFilter = targetFilterQueryManagement @@ -545,13 +432,6 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button return true; } - private void updateStatusIconAfterTablePopulated() { - queryTextField.focus(); - if (!validationFailed && !Strings.isNullOrEmpty(queryTextField.getValue())) { - showValidationSuccesIcon(); - } - } - private void showCustomFiltersView() { eventBus.publish(this, CustomFilterUIEvent.SHOW_FILTER_MANAGEMENT); } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/FilterManagementView.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/FilterManagementView.java index 40e6306b0..88f7c003d 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/FilterManagementView.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/FilterManagementView.java @@ -94,8 +94,7 @@ public class FilterManagementView extends VerticalLayout implements View { void onEvent(final CustomFilterUIEvent custFilterUIEvent) { if (custFilterUIEvent == CustomFilterUIEvent.TARGET_FILTER_DETAIL_VIEW) { viewTargetFilterDetailLayout(); - } else if (custFilterUIEvent == CustomFilterUIEvent.CREATE_NEW_FILTER_CLICK - || custFilterUIEvent == CustomFilterUIEvent.FILTER_TARGET_BY_QUERY) { + } else if (custFilterUIEvent == CustomFilterUIEvent.CREATE_NEW_FILTER_CLICK) { this.getUI().access(() -> viewCreateTargetFilterLayout()); } else if (custFilterUIEvent == CustomFilterUIEvent.EXIT_CREATE_OR_UPDATE_FILTRER_VIEW || custFilterUIEvent == CustomFilterUIEvent.SHOW_FILTER_MANAGEMENT) { diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/FilterQueryValidation.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/FilterQueryValidation.java deleted file mode 100644 index 6d1f1a7ab..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/FilterQueryValidation.java +++ /dev/null @@ -1,182 +0,0 @@ -/** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -package org.eclipse.hawkbit.ui.filtermanagement; - -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -import org.eclipse.hawkbit.repository.TargetFields; -import org.eclipse.hawkbit.repository.TargetManagement; -import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException; -import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException; -import org.eclipse.hawkbit.ui.utils.SpringContextHelper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.data.domain.PageRequest; - -import cz.jirutka.rsql.parser.ParseException; -import cz.jirutka.rsql.parser.RSQLParserException; - -/** - * - * Validates the target filter query. - * - */ -public final class FilterQueryValidation { - - private static final Logger LOGGER = LoggerFactory.getLogger(FilterQueryValidation.class); - - private FilterQueryValidation() { - - } - - /** - * method for get ExpectedTokens. - * - * @param input - * @param entityManager - * @return - */ - public static ValidationResult getExpectedTokens(final String input) { - - final TokenDescription tokenDesc = new TokenDescription(); - final ValidationResult result = new ValidationResult(); - final List expectedTokens = new ArrayList<>(); - try { - - final TargetManagement management = SpringContextHelper.getBean(TargetManagement.class); - management.findTargetsAll(input, new PageRequest(0, 100)); - } catch (final RSQLParameterSyntaxException ex) { - setExceptionDetails(new Exception(ex.getCause().getCause()), expectedTokens, result, tokenDesc); - result.setMessage(getCustomMessage(ex.getCause().getMessage(), result.getExpectedTokens())); - result.setIsValidationFailed(Boolean.TRUE); - LOGGER.trace("Syntax exception on parsing :", ex); - } catch (final RSQLParserException ex) { - setExceptionDetails(ex, expectedTokens, result, tokenDesc); - result.setMessage(getCustomMessage(ex.getMessage(), result.getExpectedTokens())); - result.setIsValidationFailed(Boolean.TRUE); - LOGGER.trace("Exception on parsing :", ex); - } catch (final IllegalArgumentException ex) { - result.setMessage(getCustomMessage(ex.getMessage(), null)); - result.setIsValidationFailed(Boolean.TRUE); - LOGGER.trace("Illegal argument on parsing :", ex); - } catch (final RSQLParameterUnsupportedFieldException ex) { - result.setMessage(getCustomMessage(ex.getMessage(), null)); - result.setIsValidationFailed(Boolean.TRUE); - LOGGER.trace("Unsupported field on parsing :", ex); - } - return result; - - } - - private static void setExceptionDetails(final Exception ex, final List expectedTokens, - final ValidationResult result, final TokenDescription tokenDesc) { - for (final Integer node : getNextTokens(ex)) { - if (node != 12) { - expectedTokens.add(tokenDesc.getTokenImage()[node]); - } - } - final List customExpectTokenList = processExpectedTokens(getNextTokens(ex)); - if (!customExpectTokenList.isEmpty()) { - result.setExpectedTokens(customExpectTokenList); - } else { - result.setExpectedTokens(expectedTokens); - } - } - - /** - * method for process ExpectedTokens. - * - * @param expectedTokens - * @return - */ - // Exception squid:S2095 - see - // https://jira.sonarsource.com/browse/SONARJAVA-1478 - @SuppressWarnings({ "squid:S2095" }) - public static List processExpectedTokens(final List expectedTokens) { - final List expectToken = new ArrayList<>(); - if (expectedTokens.size() == 2 && expectedTokens.contains(9) && expectedTokens.contains(4)) { - final List expectedFieldList = Arrays.stream(TargetFields.values()).map(v -> v.name().toLowerCase()) - .collect(Collectors.toList()); - expectToken.addAll(expectedFieldList); - expectToken.add("assignedds.name"); - expectToken.add("assignedds.version"); - } - return expectToken; - } - - /** - * Method To Get Next Token. - * - * @param e - * . - * @return list. - */ - public static List getNextTokens(final Exception e) { - Throwable parseException = e.getCause(); - final List listTokens = new ArrayList<>(); - if (parseException != null) { - do { - if (parseException instanceof ParseException) { - try { - Field declaredField; - declaredField = parseException.getClass().getDeclaredField("expectedTokenSequences"); - int[][] tokens; - tokens = (int[][]) declaredField.get(parseException); - for (final int[] is : tokens) { - for (final int i : is) { - listTokens.add(i); - } - } - return listTokens; - } catch (SecurityException | NoSuchFieldException | IllegalArgumentException - | IllegalAccessException ex) { - LOGGER.info("Exception on parsing :", ex); - } - - } else { - return listTokens; - } - } while ((parseException = parseException.getCause()) != null); - } - return Collections.emptyList(); - } - - /** - * To Get Custom Message. - * - * @param message - * @param expectedTokens - * @return String. - */ - public static String getCustomMessage(final String message, final List expectedTokens) { - String builder = message; - if (message.contains(":")) { - builder = message.substring(message.indexOf(':') + 1, message.length()); - if (builder.indexOf("Was expecting") != -1) { - builder = builder.substring(0, builder.lastIndexOf("Was expecting")); - } - if (null != expectedTokens && !expectedTokens.isEmpty()) { - final StringBuilder tokens = new StringBuilder(); - expectedTokens.stream().forEach(value -> tokens.append(value + ",")); - builder = builder.concat("Was expecting :" + tokens.toString().substring(0, tokens.length() - 1)); - } - builder = builder.replace('\r', ' '); - builder = builder.replace('\n', ' '); - builder = builder.replaceAll(">", " "); - builder = builder.replaceAll("<", " "); - } - return builder; - } - -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterHeader.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterHeader.java index ada3e600a..a58b40a98 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterHeader.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterHeader.java @@ -114,6 +114,7 @@ public class TargetFilterHeader extends VerticalLayout { } private void addNewFilter() { + filterManagementUIState.setTfQuery(null); filterManagementUIState.setFilterQueryValue(null); filterManagementUIState.setCreateFilterBtnClicked(true); eventBus.publish(this, CustomFilterUIEvent.CREATE_NEW_FILTER_CLICK); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterTable.java index 5ee3be084..010e37bde 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterTable.java @@ -225,7 +225,6 @@ public class TargetFilterTable extends Table { final String targetFilterName = (String) ((Button) event.getComponent()).getData(); final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement .findTargetFilterQueryByName(targetFilterName); - filterManagementUIState.setTfQuery(targetFilterQuery); filterManagementUIState.setFilterQueryValue(targetFilterQuery.getQuery()); filterManagementUIState.setEditViewDisplayed(true); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TextFieldSuggestionBox.gwt.xml b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TextFieldSuggestionBox.gwt.xml new file mode 100644 index 000000000..43f76950d --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TextFieldSuggestionBox.gwt.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TextFieldSuggestionBox.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TextFieldSuggestionBox.java new file mode 100644 index 000000000..3c3b63bee --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TextFieldSuggestionBox.java @@ -0,0 +1,83 @@ +/** + * 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.ui.filtermanagement; + +import java.util.stream.Collectors; + +import org.eclipse.hawkbit.repository.rsql.RsqlValidationOracle; +import org.eclipse.hawkbit.repository.rsql.SuggestionContext; +import org.eclipse.hawkbit.repository.rsql.ValidationOracleContext; +import org.eclipse.hawkbit.ui.filtermanagement.client.SuggestTokenDto; +import org.eclipse.hawkbit.ui.filtermanagement.client.SuggestionContextDto; +import org.eclipse.hawkbit.ui.filtermanagement.client.TextFieldSuggestionBoxClientRpc; +import org.eclipse.hawkbit.ui.filtermanagement.client.TextFieldSuggestionBoxServerRpc; + +import com.vaadin.server.AbstractExtension; +import com.vaadin.ui.TextField; + +/** + * Extension for the AutoCompleteTexfield. + * + */ +public class TextFieldSuggestionBox extends AbstractExtension implements TextFieldSuggestionBoxServerRpc { + + private static final long serialVersionUID = 1L; + private final transient RsqlValidationOracle rsqlValidationOracle; + private final AutoCompleteTextFieldComponent autoCompleteTextFieldComponent; + + /** + * Constructor. + * + * @param autoCompleteTextFieldComponent + * @param rsqlValidationOracle + * the suggestion oracle where to retrieve the suggestions from + */ + public TextFieldSuggestionBox(final RsqlValidationOracle rsqlValidationOracle, + final AutoCompleteTextFieldComponent autoCompleteTextFieldComponent) { + this.rsqlValidationOracle = rsqlValidationOracle; + this.autoCompleteTextFieldComponent = autoCompleteTextFieldComponent; + + registerRpc(this, TextFieldSuggestionBoxServerRpc.class); + } + + /** + * Add this extension to the target connector. This method is protected to + * allow subclasses to require a more specific type of target. + * + * @param target + * the connector to attach this extension to + */ + public void extend(final TextField target) { + super.extend(target); + } + + @Override + public void suggest(final String text, final int cursor) { + final ValidationOracleContext suggest = rsqlValidationOracle.suggest(text, cursor); + updateValidationIcon(suggest, text); + getRpcProxy(TextFieldSuggestionBoxClientRpc.class).showSuggestions(mapToDto(suggest.getSuggestionContext())); + } + + private static SuggestionContextDto mapToDto(final SuggestionContext suggestionContext) { + return new SuggestionContextDto(suggestionContext.getCursorPosition(), + suggestionContext.getSuggestions().stream() + .filter(suggestion -> suggestion.getTokenImageName() == null + || suggestion.getSuggestion().contains(suggestion.getTokenImageName())) + .map(suggestion -> new SuggestTokenDto(suggestion.getStart(), suggestion.getEnd(), + suggestion.getSuggestion())) + .collect(Collectors.toList())); + + } + + private void updateValidationIcon(final ValidationOracleContext suggest, final String text) { + final String errorMessage = (suggest.getSyntaxErrorContext() != null) + ? suggest.getSyntaxErrorContext().getErrorMessage() : null; + autoCompleteTextFieldComponent.onQueryFilterChange(text, !suggest.isSyntaxError(), errorMessage); + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TokenDescription.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TokenDescription.java deleted file mode 100644 index 81f222cee..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TokenDescription.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -package org.eclipse.hawkbit.ui.filtermanagement; - -import java.util.Arrays; - -/** - * - * Available token details. - * - * - * - */ -public class TokenDescription { - - /** Literal token values. */ - private static final String[] TOKEN_IMAGE = { "", "\" \"", "\"\\t\"", "", "", - "", "", "", "", "\"(\"", "\")\"", "<==>|", ">=|<=", }; - - public String[] getTokenImage() { - return Arrays.copyOf(TOKEN_IMAGE, TOKEN_IMAGE.length); - } - -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/ValidationResult.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/ValidationResult.java deleted file mode 100644 index 68d80fc62..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/ValidationResult.java +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -package org.eclipse.hawkbit.ui.filtermanagement; - -import java.util.ArrayList; -import java.util.List; - -/** - * Query validation result with expected token on error. - * - * - * - */ -public class ValidationResult { - - private List expectedTokens = new ArrayList<>(); - - private String message; - - private Boolean isValidationFailed = Boolean.FALSE; - - /** - * @return the isValidationFailed - */ - public Boolean getIsValidationFailed() { - return isValidationFailed; - } - - /** - * @param isValidationFailed - * the isValidationFailed to set - */ - public void setIsValidationFailed(final Boolean isValidationFailed) { - this.isValidationFailed = isValidationFailed; - } - - /** - * @return the expectedTokens - */ - public List getExpectedTokens() { - return expectedTokens; - } - - /** - * @param expectedTokens - * the expectedTokens to set - */ - public void setExpectedTokens(final List expectedTokens) { - this.expectedTokens = expectedTokens; - } - - /** - * @return the message - */ - public String getMessage() { - return message; - } - - /** - * @param message - * the message to set - */ - public void setMessage(final String message) { - this.message = message; - } - -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/AutoCompleteTextFieldConnector.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/AutoCompleteTextFieldConnector.java new file mode 100644 index 000000000..949b2a39d --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/AutoCompleteTextFieldConnector.java @@ -0,0 +1,137 @@ +/** + * 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.ui.filtermanagement.client; + +import java.util.List; + +import org.eclipse.hawkbit.ui.filtermanagement.TextFieldSuggestionBox; + +import com.google.gwt.event.dom.client.KeyCodes; +import com.google.gwt.event.dom.client.KeyUpEvent; +import com.google.gwt.event.dom.client.KeyUpHandler; +import com.google.gwt.user.client.ui.MenuItem; +import com.vaadin.client.ComponentConnector; +import com.vaadin.client.ServerConnector; +import com.vaadin.client.extensions.AbstractExtensionConnector; +import com.vaadin.client.ui.VOverlay; +import com.vaadin.client.ui.VTextField; +import com.vaadin.shared.ui.Connect; + +/** + * Connector for the AutoCompleteTextField which automatically listens to + * key-events to show pop-up panel with entered suggestions based on the + * {@link TextFieldSuggestionBoxServerRpc} call. + * + */ +@SuppressWarnings({ "deprecation", "squid:CallToDeprecatedMethod" }) +// need to use VOverlay because otherwise it's not in the correct theme +// widget @see com.vaadin.client.ui.VOverlay.getOverlayContainer() +@Connect(TextFieldSuggestionBox.class) +public class AutoCompleteTextFieldConnector extends AbstractExtensionConnector { + + private static final long serialVersionUID = 1L; + + private final transient SuggestionsSelectList select = new SuggestionsSelectList(); + private transient VTextField textFieldWidget; + + private final TextFieldSuggestionBoxServerRpc rpc = getRpcProxy(TextFieldSuggestionBoxServerRpc.class); + + private final transient VOverlay panel = new VOverlay(true, false, true); + + @Override + protected void init() { + super.init(); + + registerRpc(TextFieldSuggestionBoxClientRpc.class, new TextFieldSuggestionBoxClientRpc() { + private static final long serialVersionUID = 1L; + + @Override + public void showSuggestions(final SuggestionContextDto suggestContext) { + select.clearItems(); + if (suggestContext != null) { + final List suggestions = suggestContext.getSuggestions(); + if (suggestions != null && !suggestions.isEmpty()) { + select.addItems(suggestions, textFieldWidget, panel, rpc); + panel.showRelativeTo(textFieldWidget); + select.moveSelectionDown(); + return; + } + } + panel.hide(); + } + }); + } + + @Override + protected void extend(final ServerConnector target) { + textFieldWidget = (VTextField) ((ComponentConnector) target).getWidget(); + textFieldWidget.setImmediate(true); + textFieldWidget.textChangeEventMode = "EAGER"; + panel.setWidget(select); + panel.setStyleName("suggestion-popup"); + panel.setOwner(textFieldWidget); + + textFieldWidget.addKeyUpHandler(new KeyUpHandler() { + @Override + public void onKeyUp(final KeyUpEvent event) { + if (panel.isAttached()) { + handlePanelEventDelegation(event); + } else { + doAskForSuggestion(); + } + } + }); + } + + private void handlePanelEventDelegation(final KeyUpEvent event) { + switch (event.getNativeKeyCode()) { + case KeyCodes.KEY_DOWN: + arrowKeyDown(event); + break; + case KeyCodes.KEY_UP: + arrorKeyUp(event); + break; + case KeyCodes.KEY_ESCAPE: + escapeKey(); + break; + case KeyCodes.KEY_ENTER: + enterKey(); + break; + default: + doAskForSuggestion(); + } + } + + private void escapeKey() { + panel.hide(); + } + + private void enterKey() { + final MenuItem item = select.getSelectedItem(); + if (item != null) { + item.getScheduledCommand().execute(); + } + } + + private void arrorKeyUp(final KeyUpEvent event) { + select.moveSelectionUp(); + event.preventDefault(); + event.stopPropagation(); + } + + private void arrowKeyDown(final KeyUpEvent event) { + select.moveSelectionDown(); + event.preventDefault(); + event.stopPropagation(); + } + + private void doAskForSuggestion() { + rpc.suggest(textFieldWidget.getValue(), textFieldWidget.getCursorPos()); + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/SuggestTokenDto.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/SuggestTokenDto.java new file mode 100644 index 000000000..8291fa0bf --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/SuggestTokenDto.java @@ -0,0 +1,68 @@ +package org.eclipse.hawkbit.ui.filtermanagement.client; + +import java.io.Serializable; + +/** + * A suggestion which contains the start and the end character position of the + * suggested token of the suggestion of the token and the actual suggestion. + */ +public class SuggestTokenDto implements Serializable { + + private static final long serialVersionUID = 1L; + + private int start; + private int end; + private String suggestion; + + /** + * Default constructor. + */ + public SuggestTokenDto() { + // necessary for java serialization with GWT. + } + + /** + * Constructor. + * + * @param start + * the character position of the start of the token + * @param end + * the character position of the end of the token + * @param suggestion + * the token suggestion + */ + public SuggestTokenDto(final int start, final int end, final String suggestion) { + this.start = start; + this.end = end; + this.suggestion = suggestion; + } + + public int getStart() { + return start; + } + + public int getEnd() { + return end; + } + + public String getSuggestion() { + return suggestion; + } + + public void setStart(final int start) { + this.start = start; + } + + public void setEnd(final int end) { + this.end = end; + } + + public void setSuggestion(final String suggestion) { + this.suggestion = suggestion; + } + + @Override + public String toString() { + return "SuggestTokenDto [start=" + start + ", end=" + end + ", suggestion=" + suggestion + "]"; + } +} \ No newline at end of file diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/SuggestionContextDto.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/SuggestionContextDto.java new file mode 100644 index 000000000..ec5326d28 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/SuggestionContextDto.java @@ -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.ui.filtermanagement.client; + +import java.io.Serializable; +import java.util.List; + +public class SuggestionContextDto implements Serializable { + + private static final long serialVersionUID = 1L; + + private int cursorPosition; + private List suggestions; + + /** + * Default constructor. + */ + public SuggestionContextDto() { + // necessary for java serialization with GWT. + } + + /** + * Constructor. + * + * @param rsqlQuery + * the original RSQL based query the suggestions based on + * @param cursorPosition + * the current cursor position + * @param suggestions + * the suggestions for the current cursor position + */ + public SuggestionContextDto(final int cursorPosition, final List suggestions) { + this.cursorPosition = cursorPosition; + this.suggestions = suggestions; + } + + public List getSuggestions() { + return suggestions; + } + + public int getCursorPosition() { + return cursorPosition; + } + + public void setCursorPosition(final int cursorPosition) { + this.cursorPosition = cursorPosition; + } + + public void setSuggestions(final List suggestions) { + this.suggestions = suggestions; + } + + @Override + public String toString() { + return "SuggestionContextDto [cursorPosition=" + cursorPosition + ", suggestions=" + suggestions + "]"; + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/SuggestionsSelectList.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/SuggestionsSelectList.java new file mode 100644 index 000000000..e51c1c8fe --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/SuggestionsSelectList.java @@ -0,0 +1,116 @@ +/** + * 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.ui.filtermanagement.client; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.google.gwt.aria.client.Roles; +import com.google.gwt.core.client.Scheduler.ScheduledCommand; +import com.google.gwt.user.client.ui.MenuBar; +import com.google.gwt.user.client.ui.MenuItem; +import com.google.gwt.user.client.ui.PopupPanel; +import com.vaadin.client.WidgetUtil; +import com.vaadin.client.ui.VTextField; + +/** + * The suggestion list within the suggestion pop-up panel. + */ +public class SuggestionsSelectList extends MenuBar { + + public static final String CLASSNAME = "autocomplete"; + private final Map tokenMap = new HashMap<>(); + + /** + * Constructor. + */ + public SuggestionsSelectList() { + super(true); + setFocusOnHoverEnabled(false); + } + + /** + * Adds suggestions to the suggestion menu bar. + * + * @param suggestions + * the suggestions to be added + * @param textFieldWidget + * the text field which the suggestion is attached to to bring + * back the focus after selection + * @param popupPanel + * pop-up panel where the menu bar is shown to hide it after + * selection + * @param suggestionServerRpc + * server RPC to ask for new suggestion after a selection + */ + public void addItems(final List suggestions, final VTextField textFieldWidget, + final PopupPanel popupPanel, final TextFieldSuggestionBoxServerRpc suggestionServerRpc) { + for (int index = 0; index < suggestions.size(); index++) { + final SuggestTokenDto suggestToken = suggestions.get(index); + final MenuItem mi = new MenuItem(suggestToken.getSuggestion(), true, new ScheduledCommand() { + @Override + public void execute() { + final String tmpSuggestion = suggestToken.getSuggestion(); + final TokenStartEnd tokenStartEnd = tokenMap.get(tmpSuggestion); + final String text = textFieldWidget.getValue(); + final StringBuilder builder = new StringBuilder(text); + builder.replace(tokenStartEnd.getStart(), tokenStartEnd.getEnd() + 1, tmpSuggestion); + textFieldWidget.setValue(builder.toString(), true); + popupPanel.hide(); + textFieldWidget.setFocus(true); + suggestionServerRpc.suggest(builder.toString(), textFieldWidget.getCursorPos()); + } + }); + tokenMap.put(suggestToken.getSuggestion(), + new TokenStartEnd(suggestToken.getStart(), suggestToken.getEnd())); + Roles.getListitemRole().set(mi.getElement()); + WidgetUtil.sinkOnloadForImages(mi.getElement()); + addItem(mi); + } + } + + @Override + public void setStyleName(final String style) { + super.setStyleName(style + "-" + CLASSNAME); + } + + @Override + public MenuItem getSelectedItem() { + return super.getSelectedItem(); + } + + /** + * Suggestion Token start and end index. + * + */ + public static final class TokenStartEnd { + final int start; + final int end; + + /** + * Constructor. + * + * @param start + * @param end + */ + public TokenStartEnd(final int start, final int end) { + this.start = start; + this.end = end; + } + + public int getStart() { + return start; + } + + public int getEnd() { + return end; + } + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/TextFieldSuggestionBoxClientRpc.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/TextFieldSuggestionBoxClientRpc.java new file mode 100644 index 000000000..d4385db6f --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/TextFieldSuggestionBoxClientRpc.java @@ -0,0 +1,32 @@ +/** + * 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.ui.filtermanagement.client; + +import com.vaadin.shared.communication.ClientRpc; + +/** + * Client RPC for the AutocompleteTextField. The Client RPC interface is used to + * make server to client calls in Vaadin. Only void methods are allowed in + * ClientRpc calls. + * + */ +@FunctionalInterface +public interface TextFieldSuggestionBoxClientRpc extends ClientRpc { + + /** + * Notifies the client about showing the given suggestions in the suggestion + * box. + * + * @param suggestionContext + * the suggestion context which contains all informations about + * showing suggestions + */ + void showSuggestions(final SuggestionContextDto suggestionContext); + +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/TextFieldSuggestionBoxServerRpc.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/TextFieldSuggestionBoxServerRpc.java new file mode 100644 index 000000000..51388a0a3 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/TextFieldSuggestionBoxServerRpc.java @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.ui.filtermanagement.client; + +import com.vaadin.shared.communication.ServerRpc; + +/** + * Server RPC for the AutoCompleteTextField. The Server RPC interface is used to + * make client to server calls in Vaadin. Only void methods are allowed in + * ServerRpc calls. + */ +@FunctionalInterface +public interface TextFieldSuggestionBoxServerRpc extends ServerRpc { + + /** + * Parses the given RSQL based query and try finding suggestions at the + * current given cursor position. When suggestions are possible the + * {@link TextFieldSuggestionBoxClientRpc#showSuggestions(org.eclipse.hawkbit.rsql.SuggestionContext)} + * is called as a callback mechanism back to the client. + * + * @param text + * the current entered text e.g. in a text field to retrieve + * suggestion for + * @param cursor + * the current cursor position + */ + void suggest(final String text, final int cursor); +} diff --git a/hawkbit-ui/src/main/resources/VAADIN/themes/hawkbit/customstyles/target-filter-query.scss b/hawkbit-ui/src/main/resources/VAADIN/themes/hawkbit/customstyles/target-filter-query.scss index 33ae2dd85..102f6a3c9 100644 --- a/hawkbit-ui/src/main/resources/VAADIN/themes/hawkbit/customstyles/target-filter-query.scss +++ b/hawkbit-ui/src/main/resources/VAADIN/themes/hawkbit/customstyles/target-filter-query.scss @@ -8,6 +8,39 @@ */ @mixin target-filter-query { +.gwt-MenuBar-autocomplete { + cursor: default; + } + + .gwt-MenuBar-autocomplete .gwt-MenuItem{ + border-radius: 3px !important; + cursor: pointer !important; + font-weight: 400 !important; + line-height: 27px !important; + padding: 0 20px 0 10px !important; + position: relative !important; + white-space: nowrap !important; + } + +.gwt-MenuBar-autocomplete .gwt-MenuItem-selected { + background-color: #197de1 !important; + background-image: linear-gradient(to bottom, #1b87e3 2%, #166ed5 98%) !important; + color: #ecf2f8 !important; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.05) !important; +} +.suggestion-popup{ + backface-visibility: hidden; + background-color: white; + border-radius: 4px; + box-shadow: 0 4px 10px 0 rgba(0, 0, 0, 0.1), 0 3px 5px 0 rgba(0, 0, 0, 0.05), 0 0 0 1px rgba(0, 0, 0, 0.09); + box-sizing: border-box; + color: #474747; + padding: 4px; + position: relative; + z-index: 1; +} + + .caption-header-layout{ padding-left:10px; } @@ -23,15 +56,17 @@ .target-filter-textfield, .target-filter-textfield:focus{ border:none !important; box-shadow: none !important; - height:26px !important; + height: 26px !important; } .error-icon{ + margin-left: 5px; color:$success-icon-color !important; padding-left:2px !important; } .success-icon{ + margin-left: 5px; color:$error-icon-color !important; padding-left:2px !important; } @@ -42,10 +77,12 @@ } .target-filter-spinner{ - @include valo-spinner( - $size: $v-font-size--small, - $color: $bosch-color-light-blue - ); + margin-top: 5px; + margin-left: 5px; + @include valo-spinner( + $size:16px, + $speed:500ms + ); } .hide-status-label { diff --git a/pom.xml b/pom.xml index 31a175bd6..6bfa81534 100644 --- a/pom.xml +++ b/pom.xml @@ -105,7 +105,7 @@ 3.4 4.1 20141113 - 2.0.0 + 2.1.0 From 9be214770290548ed4e1a112e211809915438255 Mon Sep 17 00:00:00 2001 From: Gaurav Date: Tue, 6 Sep 2016 13:41:23 +0200 Subject: [PATCH 02/43] Ordered the shortcut listener on the press of Enter key for querying the filtered data. Signed-off-by: Gaurav --- .../ui/filtermanagement/AutoCompleteTextFieldComponent.java | 1 + 1 file changed, 1 insertion(+) diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/AutoCompleteTextFieldComponent.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/AutoCompleteTextFieldComponent.java index e3d766391..9752d97c7 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/AutoCompleteTextFieldComponent.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/AutoCompleteTextFieldComponent.java @@ -86,6 +86,7 @@ public class AutoCompleteTextFieldComponent extends HorizontalLayout { @PostConstruct public void postConstruct() { new TextFieldSuggestionBox(rsqlValidationOracle, this).extend(queryTextField); + queryTextField.addShortcutListener(new EnterShortCutListener()); } /** From a42edd14ada7d959edefa5cf133fdee914d7a095 Mon Sep 17 00:00:00 2001 From: Gaurav Date: Tue, 6 Sep 2016 13:41:23 +0200 Subject: [PATCH 03/43] Fix the issue on press of Enter key for querying the filtered data but not on while selecting any suggestion by pressing enter. Also fixed the infinite looping of the progress spinner. Signed-off-by: Gaurav --- .../AutoCompleteTextFieldComponent.java | 53 +++++++++++-------- .../TextFieldSuggestionBox.java | 10 ++++ .../AutoCompleteTextFieldConnector.java | 2 + .../TextFieldSuggestionBoxServerRpc.java | 12 ++++- 4 files changed, 54 insertions(+), 23 deletions(-) diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/AutoCompleteTextFieldComponent.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/AutoCompleteTextFieldComponent.java index e3d766391..24f69d2fc 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/AutoCompleteTextFieldComponent.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/AutoCompleteTextFieldComponent.java @@ -13,6 +13,7 @@ import java.util.List; import java.util.concurrent.Executor; import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; import org.eclipse.hawkbit.repository.rsql.RsqlValidationOracle; import org.eclipse.hawkbit.ui.common.builder.TextFieldBuilder; @@ -24,8 +25,9 @@ import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.vaadin.spring.events.EventBus; +import org.vaadin.spring.events.EventScope; +import org.vaadin.spring.events.annotation.EventBusListenerMethod; -import com.vaadin.event.ShortcutListener; import com.vaadin.server.FontAwesome; import com.vaadin.shared.ui.label.ContentMode; import com.vaadin.spring.annotation.SpringComponent; @@ -85,9 +87,27 @@ public class AutoCompleteTextFieldComponent extends HorizontalLayout { */ @PostConstruct public void postConstruct() { + eventBus.subscribe(this); new TextFieldSuggestionBox(rsqlValidationOracle, this).extend(queryTextField); } + @PreDestroy + void destroy() { + eventBus.unsubscribe(this); + } + + @EventBusListenerMethod(scope = EventScope.SESSION) + void onEvent(final CustomFilterUIEvent custFUIEvent) { + if (custFUIEvent == CustomFilterUIEvent.UPDATE_TARGET_FILTER_SEARCH_ICON) { + validationIcon.setValue(FontAwesome.CHECK_CIRCLE.getHtml()); + if (!isValidationError()) { + validationIcon.setStyleName(SPUIStyleDefinitions.SUCCESS_ICON); + } else { + validationIcon.setStyleName(SPUIStyleDefinitions.ERROR_ICON); + } + } + } + /** * Clears the textfield and resets the validation icon. */ @@ -181,7 +201,6 @@ public class AutoCompleteTextFieldComponent extends HorizontalLayout { textField.setTextChangeEventMode(TextChangeEventMode.EAGER); textField.setImmediate(true); textField.setTextChangeTimeout(100); - textField.addShortcutListener(new EnterShortCutListener()); return textField; } @@ -214,27 +233,17 @@ public class AutoCompleteTextFieldComponent extends HorizontalLayout { } } - private final class EnterShortCutListener extends ShortcutListener { + /** + * Sets the spinner as progress indicator. + */ + public void showValidationInProgress() { + validationIcon.setValue(null); + validationIcon.addStyleName("show-status-label"); + validationIcon.setStyleName(SPUIStyleDefinitions.TARGET_FILTER_SEARCH_PROGRESS_INDICATOR_STYLE); + } - private static final long serialVersionUID = 1L; - - public EnterShortCutListener() { - super("Enter", KeyCode.ENTER, new int[0]); - } - - @Override - public void handleAction(final Object sender, final Object target) { - if (!isValidationError()) { - showValidationInProgress(); - executor.execute(new StatusCircledAsync(UI.getCurrent())); - } - } - - private void showValidationInProgress() { - validationIcon.setValue(null); - validationIcon.addStyleName("show-status-label"); - validationIcon.setStyleName(SPUIStyleDefinitions.TARGET_FILTER_SEARCH_PROGRESS_INDICATOR_STYLE); - } + public Executor getExecutor() { + return executor; } /** diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TextFieldSuggestionBox.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TextFieldSuggestionBox.java index 3c3b63bee..51dae8561 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TextFieldSuggestionBox.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TextFieldSuggestionBox.java @@ -20,6 +20,7 @@ import org.eclipse.hawkbit.ui.filtermanagement.client.TextFieldSuggestionBoxServ import com.vaadin.server.AbstractExtension; import com.vaadin.ui.TextField; +import com.vaadin.ui.UI; /** * Extension for the AutoCompleteTexfield. @@ -64,6 +65,15 @@ public class TextFieldSuggestionBox extends AbstractExtension implements TextFie getRpcProxy(TextFieldSuggestionBoxClientRpc.class).showSuggestions(mapToDto(suggest.getSuggestionContext())); } + @Override + public void executeQuery(final String text, final int cursor) { + if (!autoCompleteTextFieldComponent.isValidationError()) { + autoCompleteTextFieldComponent.showValidationInProgress(); + autoCompleteTextFieldComponent.getExecutor() + .execute(autoCompleteTextFieldComponent.new StatusCircledAsync(UI.getCurrent())); + } + } + private static SuggestionContextDto mapToDto(final SuggestionContext suggestionContext) { return new SuggestionContextDto(suggestionContext.getCursorPosition(), suggestionContext.getSuggestions().stream() diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/AutoCompleteTextFieldConnector.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/AutoCompleteTextFieldConnector.java index 949b2a39d..8b5812bf1 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/AutoCompleteTextFieldConnector.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/AutoCompleteTextFieldConnector.java @@ -82,6 +82,8 @@ public class AutoCompleteTextFieldConnector extends AbstractExtensionConnector { public void onKeyUp(final KeyUpEvent event) { if (panel.isAttached()) { handlePanelEventDelegation(event); + } else if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) { + rpc.executeQuery(textFieldWidget.getValue(), textFieldWidget.getCursorPos()); } else { doAskForSuggestion(); } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/TextFieldSuggestionBoxServerRpc.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/TextFieldSuggestionBoxServerRpc.java index 51388a0a3..c1fc5ca8b 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/TextFieldSuggestionBoxServerRpc.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/TextFieldSuggestionBoxServerRpc.java @@ -15,7 +15,6 @@ import com.vaadin.shared.communication.ServerRpc; * make client to server calls in Vaadin. Only void methods are allowed in * ServerRpc calls. */ -@FunctionalInterface public interface TextFieldSuggestionBoxServerRpc extends ServerRpc { /** @@ -31,4 +30,15 @@ public interface TextFieldSuggestionBoxServerRpc extends ServerRpc { * the current cursor position */ void suggest(final String text, final int cursor); + + /** + * Executes the query text to get the filtered data. + * + * @param text + * the current entered text e.g. in a text field to retrieve + * suggestion for + * @param cursor + * the current cursor position + */ + void executeQuery(final String text, final int cursor); } From 903e4103aacd611f5f5306e1ebed6adc973844b6 Mon Sep 17 00:00:00 2001 From: Gaurav Date: Wed, 7 Sep 2016 12:39:03 +0200 Subject: [PATCH 04/43] Fixed the compilation error. Signed-off-by: Gaurav --- .../ui/filtermanagement/AutoCompleteTextFieldComponent.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/AutoCompleteTextFieldComponent.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/AutoCompleteTextFieldComponent.java index 7154442a4..24f69d2fc 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/AutoCompleteTextFieldComponent.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/AutoCompleteTextFieldComponent.java @@ -89,7 +89,6 @@ public class AutoCompleteTextFieldComponent extends HorizontalLayout { public void postConstruct() { eventBus.subscribe(this); new TextFieldSuggestionBox(rsqlValidationOracle, this).extend(queryTextField); -<<<<<<< HEAD } @PreDestroy @@ -107,9 +106,6 @@ public class AutoCompleteTextFieldComponent extends HorizontalLayout { validationIcon.setStyleName(SPUIStyleDefinitions.ERROR_ICON); } } -======= - queryTextField.addShortcutListener(new EnterShortCutListener()); ->>>>>>> branch 'feature_rsql_parser_suggestion' of https://github.com/bsinno/hawkbit/ } /** From 88fb28b587f4c20122ffbddd82e728511dc6c617 Mon Sep 17 00:00:00 2001 From: Gaurav Date: Wed, 7 Sep 2016 16:38:35 +0200 Subject: [PATCH 05/43] Fix for the targetFilterQuery not getting set while updating the filter. Signed-off-by: Gaurav --- .../eclipse/hawkbit/ui/filtermanagement/TargetFilterTable.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterTable.java index 010e37bde..caf6976fd 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterTable.java @@ -226,7 +226,7 @@ public class TargetFilterTable extends Table { final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement .findTargetFilterQueryByName(targetFilterName); filterManagementUIState.setFilterQueryValue(targetFilterQuery.getQuery()); - + filterManagementUIState.setTfQuery(targetFilterQuery); filterManagementUIState.setEditViewDisplayed(true); eventBus.publish(this, CustomFilterUIEvent.TARGET_FILTER_DETAIL_VIEW); } From 9bb8459f3704470b16712c66e62bd7ab0a2543d4 Mon Sep 17 00:00:00 2001 From: Michael Hirsch Date: Mon, 19 Sep 2016 13:23:18 +0200 Subject: [PATCH 06/43] add missing license header Signed-off-by: Michael Hirsch --- .../repository/jpa/rsql/RsqlParserValidationOracle.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlParserValidationOracle.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlParserValidationOracle.java index 8eb21400f..aa31bb4da 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlParserValidationOracle.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlParserValidationOracle.java @@ -1,3 +1,11 @@ +/** + * 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.jpa.rsql; import java.util.ArrayList; From 848632bd0bee9a92b9e758e5a8eeaa5584eadb66 Mon Sep 17 00:00:00 2001 From: Michael Hirsch Date: Mon, 19 Sep 2016 15:00:25 +0200 Subject: [PATCH 07/43] add missing license header Signed-off-by: Michael Hirsch --- .../java/org/eclipse/hawkbit/ui/AppWidgetSet.gwt.xml | 12 ++++++++++-- .../filtermanagement/TextFieldSuggestionBox.gwt.xml | 10 ++++++++++ .../ui/filtermanagement/client/SuggestTokenDto.java | 8 ++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/AppWidgetSet.gwt.xml b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/AppWidgetSet.gwt.xml index 2141eb6b1..5479eab26 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/AppWidgetSet.gwt.xml +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/AppWidgetSet.gwt.xml @@ -1,6 +1,14 @@ - + diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TextFieldSuggestionBox.gwt.xml b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TextFieldSuggestionBox.gwt.xml index 43f76950d..c87a64a8a 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TextFieldSuggestionBox.gwt.xml +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TextFieldSuggestionBox.gwt.xml @@ -1,4 +1,14 @@ + diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/SuggestTokenDto.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/SuggestTokenDto.java index 8291fa0bf..9dbe07d36 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/SuggestTokenDto.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/SuggestTokenDto.java @@ -1,3 +1,11 @@ +/** + * 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.ui.filtermanagement.client; import java.io.Serializable; From bacaed8482c83b73298bb3afe31e5eff78efdf47 Mon Sep 17 00:00:00 2001 From: kaizimmerm Date: Tue, 20 Sep 2016 13:35:52 +0200 Subject: [PATCH 08/43] Collection footprint optimization. Sys to Ui event bus memory leak fix. Signed-off-by: kaizimmerm --- .../hawkbit/ui/filter/FilterExpression.java | 27 ------- .../eclipse/hawkbit/ui/filter/Filters.java | 77 ------------------- .../ui/filter/target/CustomTargetFilter.java | 48 ------------ .../filter/target/TargetSearchTextFilter.java | 67 ---------------- .../ui/filter/target/TargetStatusFilter.java | 42 ---------- .../ui/filter/target/TargetTagFilter.java | 54 ------------- 6 files changed, 315 deletions(-) delete mode 100644 hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/FilterExpression.java delete mode 100644 hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/Filters.java delete mode 100644 hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/target/CustomTargetFilter.java delete mode 100644 hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/target/TargetSearchTextFilter.java delete mode 100644 hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/target/TargetStatusFilter.java delete mode 100644 hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/target/TargetTagFilter.java diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/FilterExpression.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/FilterExpression.java deleted file mode 100644 index 853c6f095..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/FilterExpression.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -package org.eclipse.hawkbit.ui.filter; - -/** - * A filter expression interface definition to implement the UI filter - * mechanism. The filter expression can evaluate if e.g. Targets should - * currently be added to the target list or if the current enabled filtered will - * filter the target and not show the newly created target. - * - */ -@FunctionalInterface -public interface FilterExpression { - - /** - * @return {@code true} if the expression evaluate that it should be - * filtered and not shown on the UI, otherwise {@code false} - */ - boolean doFilter(); - -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/Filters.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/Filters.java deleted file mode 100644 index 4b79a5654..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/Filters.java +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -package org.eclipse.hawkbit.ui.filter; - -import java.util.Arrays; -import java.util.List; - -/** - * {@link Filters} which provides the functionality to combine - * {@link FilterExpression}s. - * - * - * - * @see FilterExpression - * - */ -public final class Filters { - - /** - * private. - */ - private Filters() { - - } - - /** - * Combines the given filter to an or-expression and evaluate them. - * - * @param expressions - * the expressions to combine with an or-filter - * @return an or-combined filter expression - */ - public static FilterExpression or(final List expressions) { - return or(expressions.toArray(new FilterExpression[expressions.size()])); - } - - /** - * Combines the given filter to an or-expression and evaluate them. - * - * @param expressions - * the expressions to combine with an or-filter - * @return an or-combined filter expression - */ - public static FilterExpression or(final FilterExpression... expressions) { - return new OrFilterExpression(expressions); - } - - private static final class OrFilterExpression implements FilterExpression { - - private final FilterExpression[] expresssions; - - private OrFilterExpression(final FilterExpression[] expresssions) { - this.expresssions = Arrays.copyOf(expresssions, expresssions.length); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.hawkbit.server.ui.filter.FilterExpression#evaluate() - */ - @Override - public boolean doFilter() { - for (final FilterExpression filterExpression : expresssions) { - if (filterExpression.doFilter()) { - return true; - } - } - return false; - } - } -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/target/CustomTargetFilter.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/target/CustomTargetFilter.java deleted file mode 100644 index b0aadc01d..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/target/CustomTargetFilter.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -package org.eclipse.hawkbit.ui.filter.target; - -import java.util.Optional; - -import org.eclipse.hawkbit.repository.model.TargetFilterQuery; -import org.eclipse.hawkbit.ui.filter.FilterExpression; - -/** - * Checks if custom target filter is applied. - * - */ -public class CustomTargetFilter implements FilterExpression { - - private final Optional targetFilterQuery; - - /** - * Initialize. - * - * @param targetFilterQuery - * custom target filter applied - */ - public CustomTargetFilter(final Optional targetFilterQuery) { - this.targetFilterQuery = targetFilterQuery; - - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.hawkbit.ui.filter.FilterExpression#doFilter() - */ - @Override - public boolean doFilter() { - if (!targetFilterQuery.isPresent()) { - return false; - } - return true; - } - -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/target/TargetSearchTextFilter.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/target/TargetSearchTextFilter.java deleted file mode 100644 index 64e9067e9..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/target/TargetSearchTextFilter.java +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright (c) 2011-2015 Bosch Software Innovations GmbH, Germany. All rights reserved. - */ -package org.eclipse.hawkbit.ui.filter.target; - -import java.net.URI; - -import org.eclipse.hawkbit.repository.model.Target; -import org.eclipse.hawkbit.ui.filter.FilterExpression; - -/** - * - * - * - */ -public class TargetSearchTextFilter implements FilterExpression { - - private final Target target; - private final String searchTextUpper; - - /** - * @param target - * the target to check against the search text - * @param searchText - * the search text check against the given target - */ - public TargetSearchTextFilter(final Target target, final String searchText) { - this.target = target; - this.searchTextUpper = searchText.toUpperCase(); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.hawkbit.server.ui.filter.FilterExpression#evaluate() - */ - @Override - public boolean doFilter() { - return !(descriptionIgnoreCase() || nameIgnoreCase() || controllerIdIgnoreCase() || ipAddressIgnoreCase()); - } - - private boolean descriptionIgnoreCase() { - if (target.getDescription() == null) { - return false; - } - return target.getDescription().toUpperCase().contains(searchTextUpper); - } - - private boolean nameIgnoreCase() { - if (target.getName() == null) { - return false; - } - return target.getName().toUpperCase().contains(searchTextUpper); - } - - private boolean controllerIdIgnoreCase() { - return target.getControllerId().toUpperCase().contains(searchTextUpper); - } - - private boolean ipAddressIgnoreCase() { - final URI targetAddress = target.getTargetInfo().getAddress(); - if (targetAddress == null || targetAddress.getHost() == null) { - return false; - } - return targetAddress.getHost().toUpperCase().contains(searchTextUpper); - } -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/target/TargetStatusFilter.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/target/TargetStatusFilter.java deleted file mode 100644 index eee7156e3..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/target/TargetStatusFilter.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) 2011-2015 Bosch Software Innovations GmbH, Germany. All rights reserved. - */ -package org.eclipse.hawkbit.ui.filter.target; - -import java.util.List; - -import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; -import org.eclipse.hawkbit.ui.filter.FilterExpression; - -/** - * - * - * - */ -public class TargetStatusFilter implements FilterExpression { - - private final List targetUpdateStatus; - - /** - * @param target - * the target to check the update status against - * @param updateStatus - * the target update status to check against the given target - */ - public TargetStatusFilter(final List updateStatus) { - this.targetUpdateStatus = updateStatus; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.hawkbit.server.ui.filter.FilterExpression#evaluate() - */ - @Override - public boolean doFilter() { - if (targetUpdateStatus.isEmpty()) { - return false; - } - return true; - } -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/target/TargetTagFilter.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/target/TargetTagFilter.java deleted file mode 100644 index 5516c279e..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/target/TargetTagFilter.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright (c) 2011-2015 Bosch Software Innovations GmbH, Germany. All rights reserved. - */ -package org.eclipse.hawkbit.ui.filter.target; - -import java.util.Collection; -import java.util.List; -import java.util.stream.Collectors; - -import org.eclipse.hawkbit.repository.model.Target; -import org.eclipse.hawkbit.ui.filter.FilterExpression; -import org.springframework.util.CollectionUtils; - -/** - * - * - * - */ -public class TargetTagFilter implements FilterExpression { - - private final Target target; - private final Collection tags; - private final boolean noTag; - - /** - * @param target - * the target to check the filter against - * @param tags - * the tags to check the target against it - * @param noTag - * {@code true} indicates that targets which have no tags should - * not be filtered, otherwise {@code false} - */ - public TargetTagFilter(final Target target, final Collection tags, final boolean noTag) { - this.target = target; - this.tags = tags; - this.noTag = noTag; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.hawkbit.server.ui.filter.FilterExpression#evaluate() - */ - @Override - public boolean doFilter() { - final List targetTags = target.getTags().stream().map(targetTag -> targetTag.getName()) - .collect(Collectors.toList()); - if (targetTags.isEmpty() || (noTag && targetTags.isEmpty())) { - return false; - } - return !CollectionUtils.containsAny(targetTags, tags); - } -} From 66297d7c27c31056d1e1d2d7545f9d30eb328af8 Mon Sep 17 00:00:00 2001 From: kaizimmerm Date: Tue, 20 Sep 2016 13:40:51 +0200 Subject: [PATCH 09/43] reduced empty collections. Fixed Ui eventbus memory leak. Signed-off-by: kaizimmerm --- .../java/org/eclipse/hawkbit/app/MyUI.java | 8 +- .../simulator/amqp/AmqpConfiguration.java | 7 +- .../java/org/eclipse/hawkbit/app/MyUI.java | 8 +- .../rest/resource/DataConversionHelper.java | 9 +- .../hawkbit/amqp/AmqpConfiguration.java | 5 +- .../amqp/AmqpDeadletterProperties.java | 7 +- .../rollout/MgmtRolloutResponseBody.java | 3 +- .../resource/MgmtDistributionSetMapper.java | 55 ++++----- .../resource/MgmtDistributionSetResource.java | 3 +- .../MgmtDistributionSetTypeMapper.java | 32 ++--- .../MgmtDistributionSetTypeResource.java | 4 +- .../mgmt/rest/resource/MgmtRolloutMapper.java | 19 +-- .../resource/MgmtSoftwareModuleMapper.java | 76 +++++------- .../resource/MgmtSoftwareModuleResource.java | 6 +- .../MgmtSoftwareModuleTypeMapper.java | 33 ++--- .../MgmtSoftwareModuleTypeResource.java | 2 +- .../mgmt/rest/resource/MgmtTargetMapper.java | 74 ++++++----- .../rest/resource/MgmtTargetResource.java | 3 +- .../MgmtDistributionSetTypeResourceTest.java | 3 +- .../MgmtSoftwareModuleResourceTest.java | 3 +- .../MgmtSoftwareModuleTypeResourceTest.java | 3 +- .../hawkbit/repository/EntityFactory.java | 38 +++--- .../repository/model/ActionStatus.java | 2 +- .../hawkbit/repository/model/Artifact.java | 2 +- .../hawkbit/repository/model/BaseEntity.java | 2 +- .../repository/model/RolloutGroup.java | 2 +- .../hawkbit/repository/model/TargetInfo.java | 3 +- .../hawkbit-repository-jpa/pom.xml | 4 + .../RepositoryApplicationConfiguration.java | 4 +- .../repository/jpa/ActionRepository.java | 3 +- .../jpa/JpaControllerManagement.java | 9 +- .../jpa/JpaDeploymentManagement.java | 2 +- .../jpa/JpaDistributionSetManagement.java | 55 ++++----- .../repository/jpa/JpaEntityFactory.java | 2 + .../jpa/JpaRolloutGroupManagement.java | 10 +- .../repository/jpa/JpaRolloutManagement.java | 8 +- .../repository/jpa/JpaSoftwareManagement.java | 47 ++++--- .../repository/jpa/JpaTagManagement.java | 17 +-- .../repository/jpa/JpaTargetManagement.java | 49 ++++---- .../ExceptionMappingAspectHandler.java | 7 +- .../jpa/model/AbstractJpaArtifact.java | 6 +- .../jpa/model/AbstractJpaBaseEntity.java | 9 +- .../jpa/model/AbstractJpaMetaData.java | 3 +- .../jpa/model/AbstractJpaNamedEntity.java | 2 +- .../AbstractJpaNamedVersionedEntity.java | 2 +- .../repository/jpa/model/AbstractJpaTag.java | 2 +- .../repository/jpa/model/JpaAction.java | 7 +- .../repository/jpa/model/JpaActionStatus.java | 21 +++- .../jpa/model/JpaActionWithStatusCount.java | 2 +- .../jpa/model/JpaDistributionSet.java | 71 +++++++++-- .../jpa/model/JpaDistributionSetTag.java | 7 +- .../jpa/model/JpaDistributionSetType.java | 39 +++++- .../repository/jpa/model/JpaRollout.java | 9 +- .../repository/jpa/model/JpaRolloutGroup.java | 18 +-- .../jpa/model/JpaSoftwareModule.java | 65 +++++----- .../repository/jpa/model/JpaTarget.java | 50 ++++++-- .../repository/jpa/model/JpaTargetInfo.java | 2 +- .../repository/jpa/model/JpaTargetTag.java | 7 +- ...ThresholdRolloutGroupSuccessCondition.java | 4 +- .../jpa/DeploymentManagementTest.java | 10 +- .../jpa/DistributionSetManagementTest.java | 6 +- .../jpa/SoftwareManagementTest.java | 10 +- .../jpa/TargetManagementSearchTest.java | 5 +- .../repository/jpa/TargetManagementTest.java | 26 ++-- .../jpa/rsql/RSQLActionFieldsTest.java | 7 +- .../details/ArtifactDetailsLayout.java | 11 +- .../event/UploadViewAcceptCriteria.java | 11 +- .../UploadViewConfirmationWindowLayout.java | 4 +- .../smtable/SoftwareModuleTable.java | 4 +- .../client/renderers/HtmlLabelRenderer.java | 25 ++-- .../dstable/DistributionSetDetails.java | 17 ++- .../dstable/DistributionSetTable.java | 5 +- .../DistributionsViewAcceptCriteria.java | 6 +- ...DistributionsConfirmationWindowLayout.java | 5 +- .../distributions/smtable/SwModuleTable.java | 4 +- .../smtype/DistSMTypeFilterButtons.java | 6 +- .../state/ManageDistUIState.java | 6 +- .../CreateOrUpdateFilterTable.java | 4 +- .../filtermanagement/TargetFilterTable.java | 3 +- .../DistributionAddUpdateWindowLayout.java | 6 +- .../management/dstable/DistributionTable.java | 4 +- .../dstag/DistributionTagButtons.java | 6 +- .../event/ManagementViewAcceptCriteria.java | 9 +- .../ManangementConfirmationWindowLayout.java | 3 +- .../management/state/ManagementUIState.java | 7 +- .../TargetBulkUpdateWindowLayout.java | 12 +- .../management/targettable/TargetTable.java | 115 ++++++------------ .../targettag/TargetFilterQueryButtons.java | 6 +- .../ui/push/DelayedEventBusPushStrategy.java | 75 ++++++++---- .../ui/rollout/rollout/RolloutListGrid.java | 5 +- .../hawkbit/ui/utils/HawkbitCommonUtil.java | 8 +- .../hawkbit/ui/utils/SPDateTimeUtil.java | 4 +- 92 files changed, 743 insertions(+), 647 deletions(-) diff --git a/examples/hawkbit-custom-theme-example/src/main/java/org/eclipse/hawkbit/app/MyUI.java b/examples/hawkbit-custom-theme-example/src/main/java/org/eclipse/hawkbit/app/MyUI.java index 5ef632d10..964994317 100644 --- a/examples/hawkbit-custom-theme-example/src/main/java/org/eclipse/hawkbit/app/MyUI.java +++ b/examples/hawkbit-custom-theme-example/src/main/java/org/eclipse/hawkbit/app/MyUI.java @@ -8,6 +8,8 @@ package org.eclipse.hawkbit.app; * http://www.eclipse.org/legal/epl-v10.html */ +import java.util.concurrent.ScheduledExecutorService; + import org.eclipse.hawkbit.ui.HawkbitUI; import org.eclipse.hawkbit.ui.UIEventProvider; import org.eclipse.hawkbit.ui.push.DelayedEventBusPushStrategy; @@ -38,8 +40,8 @@ public class MyUI extends HawkbitUI { private static final long serialVersionUID = 1L; @Autowired - public MyUI(final EventBus systemEventBus, final org.vaadin.spring.events.EventBus.SessionEventBus eventBus, - final UIEventProvider provider) { - super(new DelayedEventBusPushStrategy(eventBus, systemEventBus, provider)); + public MyUI(final ScheduledExecutorService executorService, final EventBus systemEventBus, + final org.vaadin.spring.events.EventBus.SessionEventBus eventBus, final UIEventProvider provider) { + super(new DelayedEventBusPushStrategy(executorService, eventBus, systemEventBus, provider)); } } diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/AmqpConfiguration.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/AmqpConfiguration.java index 5e9b3e049..f024f174b 100644 --- a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/AmqpConfiguration.java +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/AmqpConfiguration.java @@ -11,7 +11,6 @@ package org.eclipse.hawkbit.simulator.amqp; import static org.eclipse.hawkbit.simulator.amqp.AmqpProperties.CONFIGURATION_PREFIX; import java.time.Duration; -import java.util.HashMap; import java.util.Map; import org.slf4j.Logger; @@ -36,6 +35,8 @@ import org.springframework.context.annotation.Configuration; import org.springframework.retry.backoff.ExponentialBackOffPolicy; import org.springframework.retry.support.RetryTemplate; +import com.google.gwt.thirdparty.guava.common.collect.Maps; + /** * The spring AMQP configuration to use a AMQP for communication with SP update * server. @@ -200,13 +201,13 @@ public class AmqpConfiguration { } private Map getDeadLetterExchangeArgs() { - final Map args = new HashMap<>(); + final Map args = Maps.newHashMapWithExpectedSize(1); args.put("x-dead-letter-exchange", amqpProperties.getDeadLetterExchange()); return args; } private static Map getTTLMaxArgs() { - final Map args = new HashMap<>(); + final Map args = Maps.newHashMapWithExpectedSize(2); args.put("x-message-ttl", Duration.ofDays(1).toMillis()); args.put("x-max-length", 100_000); return args; diff --git a/examples/hawkbit-example-app/src/main/java/org/eclipse/hawkbit/app/MyUI.java b/examples/hawkbit-example-app/src/main/java/org/eclipse/hawkbit/app/MyUI.java index e5bc7535d..0316b68e5 100644 --- a/examples/hawkbit-example-app/src/main/java/org/eclipse/hawkbit/app/MyUI.java +++ b/examples/hawkbit-example-app/src/main/java/org/eclipse/hawkbit/app/MyUI.java @@ -8,6 +8,8 @@ */ package org.eclipse.hawkbit.app; +import java.util.concurrent.ScheduledExecutorService; + import org.eclipse.hawkbit.ui.HawkbitUI; import org.eclipse.hawkbit.ui.UIEventProvider; import org.eclipse.hawkbit.ui.push.DelayedEventBusPushStrategy; @@ -37,8 +39,8 @@ public class MyUI extends HawkbitUI { private static final long serialVersionUID = 1L; @Autowired - public MyUI(final EventBus systemEventBus, final org.vaadin.spring.events.EventBus.SessionEventBus eventBus, - final UIEventProvider provider) { - super(new DelayedEventBusPushStrategy(eventBus, systemEventBus, provider)); + public MyUI(final ScheduledExecutorService scheduledExecutorService, final EventBus systemEventBus, + final org.vaadin.spring.events.EventBus.SessionEventBus eventBus, final UIEventProvider provider) { + super(new DelayedEventBusPushStrategy(scheduledExecutorService, eventBus, systemEventBus, provider)); } } diff --git a/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java b/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java index 55be6f58a..3a50ede61 100644 --- a/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java +++ b/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java @@ -12,7 +12,6 @@ import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; import java.io.IOException; -import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -73,16 +72,16 @@ public final class DataConversionHelper { * of the target * @param module * the software module + * @param artifactUrlHandler + * for creating download URLs * @return a list of artifacts or a empty list. Cannot be . */ public static List createArtifacts(final String targetid, final org.eclipse.hawkbit.repository.model.SoftwareModule module, final ArtifactUrlHandler artifactUrlHandler) { - final List files = new ArrayList<>(); - module.getLocalArtifacts() - .forEach(artifact -> files.add(createArtifact(targetid, artifactUrlHandler, artifact))); - return files; + return module.getLocalArtifacts().stream() + .map(artifact -> createArtifact(targetid, artifactUrlHandler, artifact)).collect(Collectors.toList()); } private static DdiArtifact createArtifact(final String targetid, final ArtifactUrlHandler artifactUrlHandler, diff --git a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpConfiguration.java b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpConfiguration.java index 09edd41d3..b8cbb8162 100644 --- a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpConfiguration.java +++ b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpConfiguration.java @@ -9,7 +9,6 @@ package org.eclipse.hawkbit.amqp; import java.time.Duration; -import java.util.HashMap; import java.util.Map; import java.util.concurrent.Executor; import java.util.concurrent.ScheduledExecutorService; @@ -47,6 +46,8 @@ import org.springframework.retry.backoff.ExponentialBackOffPolicy; import org.springframework.retry.support.RetryTemplate; import org.springframework.util.ErrorHandler; +import com.google.common.collect.Maps; + /** * Spring configuration for AMQP 0.9 based DMF communication for indirect device * integration. @@ -306,7 +307,7 @@ public class AmqpConfiguration { } private static Map getTTLMaxArgsAuthenticationQueue() { - final Map args = new HashMap<>(); + final Map args = Maps.newHashMapWithExpectedSize(2); args.put("x-message-ttl", Duration.ofSeconds(30).toMillis()); args.put("x-max-length", 1_000); return args; diff --git a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpDeadletterProperties.java b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpDeadletterProperties.java index 7ed5e90c1..723caf44c 100644 --- a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpDeadletterProperties.java +++ b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpDeadletterProperties.java @@ -9,12 +9,13 @@ package org.eclipse.hawkbit.amqp; import java.time.Duration; -import java.util.HashMap; import java.util.Map; import org.springframework.amqp.core.Queue; import org.springframework.boot.context.properties.ConfigurationProperties; +import com.google.common.collect.Maps; + /** * Bean which holds the necessary properties for configuring the AMQP deadletter * queue. @@ -36,7 +37,7 @@ public class AmqpDeadletterProperties { * @return map which holds the properties */ public Map getDeadLetterExchangeArgs(final String exchange) { - final Map args = new HashMap<>(); + final Map args = Maps.newHashMapWithExpectedSize(1); args.put("x-dead-letter-exchange", exchange); return args; } @@ -53,7 +54,7 @@ public class AmqpDeadletterProperties { } private Map getTTLArgs() { - final Map args = new HashMap<>(); + final Map args = Maps.newHashMapWithExpectedSize(1); args.put("x-message-ttl", getTtl()); return args; } diff --git a/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/rollout/MgmtRolloutResponseBody.java b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/rollout/MgmtRolloutResponseBody.java index 79455f501..ce74b4c8d 100644 --- a/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/rollout/MgmtRolloutResponseBody.java +++ b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/rollout/MgmtRolloutResponseBody.java @@ -8,7 +8,6 @@ */ package org.eclipse.hawkbit.mgmt.json.model.rollout; -import java.util.HashMap; import java.util.Map; import org.eclipse.hawkbit.mgmt.json.model.MgmtNamedEntity; @@ -38,7 +37,7 @@ public class MgmtRolloutResponseBody extends MgmtNamedEntity { private Long totalTargets; @JsonProperty(required = true) - private final Map totalTargetsPerStatus = new HashMap<>(); + private Map totalTargetsPerStatus; /** * @return the status diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetMapper.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetMapper.java index 4a0f8d90e..acd088c85 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetMapper.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetMapper.java @@ -12,7 +12,10 @@ import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; import org.eclipse.hawkbit.mgmt.json.model.MgmtMetadata; import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet; @@ -34,10 +37,6 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule; /** * A mapper which maps repository model to RESTful model representation and * back. - * - * - * - * */ public final class MgmtDistributionSetMapper { private MgmtDistributionSetMapper() { @@ -75,15 +74,13 @@ public final class MgmtDistributionSetMapper { * to use for conversion * @return converted list of {@link DistributionSet}s */ - static List dsFromRequest(final Iterable sets, + static List dsFromRequest(final Collection sets, final SoftwareManagement softwareManagement, final DistributionSetManagement distributionSetManagement, final EntityFactory entityFactory) { - final List mappedList = new ArrayList<>(); - for (final MgmtDistributionSetRequestBodyPost dsRest : sets) { - mappedList.add(fromRequest(dsRest, softwareManagement, distributionSetManagement, entityFactory)); - } - return mappedList; + return sets.stream() + .map(dsRest -> fromRequest(dsRest, softwareManagement, distributionSetManagement, entityFactory)) + .collect(Collectors.toList()); } @@ -139,15 +136,12 @@ public final class MgmtDistributionSetMapper { */ static List fromRequestDsMetadata(final DistributionSet ds, final List metadata, final EntityFactory entityFactory) { - final List mappedList = new ArrayList<>(metadata.size()); - for (final MgmtMetadata metadataRest : metadata) { - if (metadataRest.getKey() == null) { - throw new IllegalArgumentException("the key of the metadata must be present"); - } - mappedList.add( - entityFactory.generateDistributionSetMetadata(ds, metadataRest.getKey(), metadataRest.getValue())); + if (metadata == null) { + return Collections.emptyList(); } - return mappedList; + + return metadata.stream().map(metadataRest -> entityFactory.generateDistributionSetMetadata(ds, + metadataRest.getKey(), metadataRest.getValue())).collect(Collectors.toList()); } /** @@ -196,15 +190,12 @@ public final class MgmtDistributionSetMapper { return result; } - static List toResponseDistributionSets(final Iterable sets) { - final List response = new ArrayList<>(); - if (sets != null) { - - for (final DistributionSet set : sets) { - response.add(toResponse(set)); - } + static List toResponseDistributionSets(final Collection sets) { + if (sets == null) { + return Collections.emptyList(); } - return response; + + return sets.stream().map(MgmtDistributionSetMapper::toResponse).collect(Collectors.toList()); } static MgmtMetadata toResponseDsMetadata(final DistributionSetMetadata metadata) { @@ -224,14 +215,10 @@ public final class MgmtDistributionSetMapper { } static List toResponseFromDsList(final List sets) { - final List mappedList = new ArrayList<>(); - if (sets != null) { - for (final DistributionSet set : sets) { - final MgmtDistributionSet response = toResponse(set); - - mappedList.add(response); - } + if (sets == null) { + return Collections.emptyList(); } - return mappedList; + + return sets.stream().map(MgmtDistributionSetMapper::toResponse).collect(Collectors.toList()); } } diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java index 721995d88..b49654460 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java @@ -8,6 +8,7 @@ */ package org.eclipse.hawkbit.mgmt.rest.resource; +import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -123,7 +124,7 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi { .runAsSystem(() -> this.systemManagement.getTenantMetadata().getDefaultDsType().getKey()); sets.stream().filter(ds -> ds.getType() == null).forEach(ds -> ds.setType(defaultDsKey)); - final Iterable createdDSets = this.distributionSetManagement + final Collection createdDSets = this.distributionSetManagement .createDistributionSets(MgmtDistributionSetMapper.dsFromRequest(sets, this.softwareManagement, this.distributionSetManagement, entityFactory)); diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeMapper.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeMapper.java index 675fe9df0..33b81ab73 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeMapper.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeMapper.java @@ -11,8 +11,10 @@ package org.eclipse.hawkbit.mgmt.rest.resource; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; -import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; import org.eclipse.hawkbit.mgmt.json.model.distributionsettype.MgmtDistributionSetType; import org.eclipse.hawkbit.mgmt.json.model.distributionsettype.MgmtDistributionSetTypeRequestBodyPost; @@ -39,13 +41,13 @@ final class MgmtDistributionSetTypeMapper { static List smFromRequest(final EntityFactory entityFactory, final SoftwareManagement softwareManagement, - final Iterable smTypesRest) { - final List mappedList = new ArrayList<>(); - - for (final MgmtDistributionSetTypeRequestBodyPost smRest : smTypesRest) { - mappedList.add(fromRequest(entityFactory, softwareManagement, smRest)); + final Collection smTypesRest) { + if (smTypesRest == null) { + return Collections.emptyList(); } - return mappedList; + + return smTypesRest.stream().map(smRest -> fromRequest(entityFactory, softwareManagement, smRest)) + .collect(Collectors.toList()); } static DistributionSetType fromRequest(final EntityFactory entityFactory, @@ -91,19 +93,19 @@ final class MgmtDistributionSetTypeMapper { } static List toTypesResponse(final List types) { - final List response = new ArrayList<>(); - for (final DistributionSetType dsType : types) { - response.add(toResponse(dsType)); + if (types == null) { + return Collections.emptyList(); } - return response; + + return types.stream().map(MgmtDistributionSetTypeMapper::toResponse).collect(Collectors.toList()); } static List toListResponse(final List types) { - final List response = new ArrayList<>(); - for (final DistributionSetType dsType : types) { - response.add(toResponse(dsType)); + if (types == null) { + return Collections.emptyList(); } - return response; + + return types.stream().map(MgmtDistributionSetTypeMapper::toResponse).collect(Collectors.toList()); } static MgmtDistributionSetType toResponse(final DistributionSetType type) { diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeResource.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeResource.java index d69e4d8d4..762e0e104 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeResource.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeResource.java @@ -146,7 +146,7 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR @PathVariable("distributionSetTypeId") final Long distributionSetTypeId) { final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId); - return new ResponseEntity<>(MgmtSoftwareModuleTypeMapper.toListResponse(foundType.getMandatoryModuleTypes()), + return new ResponseEntity<>(MgmtSoftwareModuleTypeMapper.toTypesResponse(foundType.getMandatoryModuleTypes()), HttpStatus.OK); } @@ -190,7 +190,7 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId); - return new ResponseEntity<>(MgmtSoftwareModuleTypeMapper.toListResponse(foundType.getOptionalModuleTypes()), + return new ResponseEntity<>(MgmtSoftwareModuleTypeMapper.toTypesResponse(foundType.getOptionalModuleTypes()), HttpStatus.OK); } diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtRolloutMapper.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtRolloutMapper.java index 7467a45b0..2a092851d 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtRolloutMapper.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtRolloutMapper.java @@ -11,8 +11,9 @@ package org.eclipse.hawkbit.mgmt.rest.resource; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; import org.eclipse.hawkbit.mgmt.json.model.rollout.MgmtRolloutCondition.Condition; import org.eclipse.hawkbit.mgmt.json.model.rollout.MgmtRolloutErrorAction.ErrorAction; @@ -48,9 +49,11 @@ final class MgmtRolloutMapper { } static List toResponseRollout(final List rollouts) { - final List result = new ArrayList<>(rollouts.size()); - rollouts.forEach(r -> result.add(toResponseRollout(r))); - return result; + if (rollouts == null) { + return Collections.emptyList(); + } + + return rollouts.stream().map(MgmtRolloutMapper::toResponseRollout).collect(Collectors.toList()); } static MgmtRolloutResponseBody toResponseRollout(final Rollout rollout) { @@ -103,9 +106,11 @@ final class MgmtRolloutMapper { } static List toResponseRolloutGroup(final List rollouts) { - final List result = new ArrayList<>(rollouts.size()); - rollouts.forEach(r -> result.add(toResponseRolloutGroup(r))); - return result; + if (rollouts == null) { + return Collections.emptyList(); + } + + return rollouts.stream().map(MgmtRolloutMapper::toResponseRolloutGroup).collect(Collectors.toList()); } static MgmtRolloutGroupResponseBody toResponseRolloutGroup(final RolloutGroup rolloutGroup) { diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleMapper.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleMapper.java index 3960fb3c2..2166a7e43 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleMapper.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleMapper.java @@ -11,8 +11,10 @@ package org.eclipse.hawkbit.mgmt.rest.resource; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; -import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; import org.eclipse.hawkbit.mgmt.json.model.MgmtMetadata; import org.eclipse.hawkbit.mgmt.json.model.artifact.MgmtArtifact; @@ -65,60 +67,46 @@ public final class MgmtSoftwareModuleMapper { } static List fromRequestSwMetadata(final EntityFactory entityFactory, - final SoftwareModule sw, final List metadata) { - final List mappedList = new ArrayList<>(metadata.size()); - for (final MgmtMetadata metadataRest : metadata) { - if (metadataRest.getKey() == null) { - throw new IllegalArgumentException("the key of the metadata must be present"); - } - mappedList.add( - entityFactory.generateSoftwareModuleMetadata(sw, metadataRest.getKey(), metadataRest.getValue())); + final SoftwareModule sw, final Collection metadata) { + if (metadata == null) { + return Collections.emptyList(); } - return mappedList; + + return metadata.stream().map(metadataRest -> entityFactory.generateSoftwareModuleMetadata(sw, + metadataRest.getKey(), metadataRest.getValue())).collect(Collectors.toList()); } static List smFromRequest(final EntityFactory entityFactory, - final Iterable smsRest, final SoftwareManagement softwareManagement) { - final List mappedList = new ArrayList<>(); - for (final MgmtSoftwareModuleRequestBodyPost smRest : smsRest) { - mappedList.add(fromRequest(entityFactory, smRest, softwareManagement)); + final Collection smsRest, final SoftwareManagement softwareManagement) { + if (smsRest == null) { + return Collections.emptyList(); } - return mappedList; + + return smsRest.stream().map(smRest -> fromRequest(entityFactory, smRest, softwareManagement)) + .collect(Collectors.toList()); } /** * Create response for sw modules. * - * @param baseSoftareModules + * @param softwareModules * the modules * @return the response */ - public static List toResponse(final List baseSoftareModules) { - final List mappedList = new ArrayList<>(); - if (baseSoftareModules != null) { - for (final SoftwareModule target : baseSoftareModules) { - final MgmtSoftwareModule response = toResponse(target); - - mappedList.add(response); - } + public static List toResponse(final Collection softwareModules) { + if (softwareModules == null) { + return Collections.emptyList(); } - return mappedList; + + return softwareModules.stream().map(MgmtSoftwareModuleMapper::toResponse).collect(Collectors.toList()); } - static List toResponseSoftwareModules(final Iterable softwareModules) { - final List response = new ArrayList<>(); - for (final SoftwareModule softwareModule : softwareModules) { - response.add(toResponse(softwareModule)); + static List toResponseSwMetadata(final Collection metadata) { + if (metadata == null) { + return Collections.emptyList(); } - return response; - } - static List toResponseSwMetadata(final List metadata) { - final List mappedList = new ArrayList<>(metadata.size()); - for (final SoftwareModuleMetadata distributionSetMetadata : metadata) { - mappedList.add(toResponseSwMetadata(distributionSetMetadata)); - } - return mappedList; + return metadata.stream().map(MgmtSoftwareModuleMapper::toResponseSwMetadata).collect(Collectors.toList()); } static MgmtMetadata toResponseSwMetadata(final SoftwareModuleMetadata metadata) { @@ -194,15 +182,11 @@ public final class MgmtSoftwareModuleMapper { return artifactRest; } - static List artifactsToResponse(final List artifacts) { - final List mappedList = new ArrayList<>(); - - if (artifacts != null) { - for (final Artifact artifact : artifacts) { - final MgmtArtifact response = toResponse(artifact); - mappedList.add(response); - } + static List artifactsToResponse(final Collection artifacts) { + if (artifacts == null) { + return Collections.emptyList(); } - return mappedList; + + return artifacts.stream().map(MgmtSoftwareModuleMapper::toResponse).collect(Collectors.toList()); } } diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResource.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResource.java index b475c473f..f3759d94f 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResource.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResource.java @@ -9,6 +9,7 @@ package org.eclipse.hawkbit.mgmt.rest.resource; import java.io.IOException; +import java.util.Collection; import java.util.List; import org.eclipse.hawkbit.mgmt.json.model.MgmtMetadata; @@ -158,12 +159,11 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi { public ResponseEntity> createSoftwareModules( @RequestBody final List softwareModules) { LOG.debug("creating {} softwareModules", softwareModules.size()); - final Iterable createdSoftwareModules = softwareManagement.createSoftwareModule( + final Collection createdSoftwareModules = softwareManagement.createSoftwareModule( MgmtSoftwareModuleMapper.smFromRequest(entityFactory, softwareModules, softwareManagement)); LOG.debug("{} softwareModules created, return status {}", softwareModules.size(), HttpStatus.CREATED); - return new ResponseEntity<>(MgmtSoftwareModuleMapper.toResponseSoftwareModules(createdSoftwareModules), - HttpStatus.CREATED); + return new ResponseEntity<>(MgmtSoftwareModuleMapper.toResponse(createdSoftwareModules), HttpStatus.CREATED); } @Override diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeMapper.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeMapper.java index 565af7eb8..e14ed27fa 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeMapper.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeMapper.java @@ -11,9 +11,10 @@ package org.eclipse.hawkbit.mgmt.rest.resource; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; -import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; import org.eclipse.hawkbit.mgmt.json.model.softwaremoduletype.MgmtSoftwareModuleType; import org.eclipse.hawkbit.mgmt.json.model.softwaremoduletype.MgmtSoftwareModuleTypeRequestBodyPost; @@ -25,9 +26,6 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleType; * A mapper which maps repository model to RESTful model representation and * back. * - * - * - * */ final class MgmtSoftwareModuleTypeMapper { @@ -37,13 +35,12 @@ final class MgmtSoftwareModuleTypeMapper { } static List smFromRequest(final EntityFactory entityFactory, - final Iterable smTypesRest) { - final List mappedList = new ArrayList<>(); - - for (final MgmtSoftwareModuleTypeRequestBodyPost smRest : smTypesRest) { - mappedList.add(fromRequest(entityFactory, smRest)); + final Collection smTypesRest) { + if (smTypesRest == null) { + return Collections.emptyList(); } - return mappedList; + + return smTypesRest.stream().map(smRest -> fromRequest(entityFactory, smRest)).collect(Collectors.toList()); } static SoftwareModuleType fromRequest(final EntityFactory entityFactory, @@ -57,20 +54,12 @@ final class MgmtSoftwareModuleTypeMapper { return result; } - static List toTypesResponse(final List types) { - final List response = new ArrayList<>(); - for (final SoftwareModuleType softwareModule : types) { - response.add(toResponse(softwareModule)); + static List toTypesResponse(final Collection types) { + if (types == null) { + return Collections.emptyList(); } - return response; - } - static List toListResponse(final Collection types) { - final List response = new ArrayList<>(); - for (final SoftwareModuleType softwareModule : types) { - response.add(toResponse(softwareModule)); - } - return response; + return types.stream().map(MgmtSoftwareModuleTypeMapper::toResponse).collect(Collectors.toList()); } static MgmtSoftwareModuleType toResponse(final SoftwareModuleType type) { diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeResource.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeResource.java index 353cf16e5..762dd7c1c 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeResource.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeResource.java @@ -72,7 +72,7 @@ public class MgmtSoftwareModuleTypeResource implements MgmtSoftwareModuleTypeRes } final List rest = MgmtSoftwareModuleTypeMapper - .toListResponse(findModuleTypessAll.getContent()); + .toTypesResponse(findModuleTypessAll.getContent()); return new ResponseEntity<>(new PagedList<>(rest, countModulesAll), HttpStatus.OK); } diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetMapper.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetMapper.java index a889edc35..014d0e9e3 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetMapper.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetMapper.java @@ -13,9 +13,11 @@ import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; import java.net.URI; import java.time.ZoneId; -import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.Date; import java.util.List; +import java.util.stream.Collectors; import org.eclipse.hawkbit.mgmt.json.model.MgmtPollStatus; import org.eclipse.hawkbit.mgmt.json.model.action.MgmtAction; @@ -92,17 +94,17 @@ public final class MgmtTargetMapper { * the targets * @return the response */ - public static List toResponseWithLinksAndPollStatus(final Iterable targets) { - final List mappedList = new ArrayList<>(); - if (targets != null) { - for (final Target target : targets) { - final MgmtTarget response = toResponse(target); - addPollStatus(target, response); - addTargetLinks(response); - mappedList.add(response); - } + public static List toResponseWithLinksAndPollStatus(final Collection targets) { + if (targets == null) { + return Collections.emptyList(); } - return mappedList; + + return targets.stream().map(target -> { + final MgmtTarget response = toResponse(target); + addPollStatus(target, response); + addTargetLinks(response); + return response; + }).collect(Collectors.toList()); } /** @@ -112,15 +114,12 @@ public final class MgmtTargetMapper { * list of targets * @return the response */ - public static List toResponse(final Iterable targets) { - final List mappedList = new ArrayList<>(); - if (targets != null) { - for (final Target target : targets) { - final MgmtTarget response = toResponse(target); - mappedList.add(response); - } + public static List toResponse(final Collection targets) { + if (targets == null) { + return Collections.emptyList(); } - return mappedList; + + return targets.stream().map(MgmtTargetMapper::toResponse).collect(Collectors.toList()); } /** @@ -173,12 +172,13 @@ public final class MgmtTargetMapper { } static List fromRequest(final EntityFactory entityFactory, - final Iterable targetsRest) { - final List mappedList = new ArrayList<>(); - for (final MgmtTargetRequestBody targetRest : targetsRest) { - mappedList.add(fromRequest(entityFactory, targetRest)); + final Collection targetsRest) { + if (targetsRest == null) { + return Collections.emptyList(); } - return mappedList; + + return targetsRest.stream().map(targetRest -> fromRequest(entityFactory, targetRest)) + .collect(Collectors.toList()); } static Target fromRequest(final EntityFactory entityFactory, final MgmtTargetRequestBody targetRest) { @@ -190,17 +190,12 @@ public final class MgmtTargetMapper { return target; } - static List toActionStatusRestResponse(final List actionStatus) { - final List mappedList = new ArrayList<>(); - - if (actionStatus != null) { - for (final ActionStatus status : actionStatus) { - final MgmtActionStatus response = toResponse(status); - mappedList.add(response); - } + static List toActionStatusRestResponse(final Collection actionStatus) { + if (actionStatus == null) { + return Collections.emptyList(); } - return mappedList; + return actionStatus.stream().map(MgmtTargetMapper::toResponse).collect(Collectors.toList()); } static MgmtAction toResponse(final String targetId, final Action action, final boolean isActive) { @@ -222,14 +217,13 @@ public final class MgmtTargetMapper { return result; } - static List toResponse(final String targetId, final List actions) { - final List mappedList = new ArrayList<>(); - - for (final Action action : actions) { - final MgmtAction response = toResponse(targetId, action, action.isActive()); - mappedList.add(response); + static List toResponse(final String targetId, final Collection actions) { + if (actions == null) { + return Collections.emptyList(); } - return mappedList; + + return actions.stream().map(action -> toResponse(targetId, action, action.isActive())) + .collect(Collectors.toList()); } private static String getNameOfActionStatusType(final Action.Status type) { diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResource.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResource.java index 5ddb74314..d0d72be40 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResource.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResource.java @@ -11,6 +11,7 @@ package org.eclipse.hawkbit.mgmt.rest.resource; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; +import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -108,7 +109,7 @@ public class MgmtTargetResource implements MgmtTargetRestApi { @Override public ResponseEntity> createTargets(@RequestBody final List targets) { LOG.debug("creating {} targets", targets.size()); - final Iterable createdTargets = this.targetManagement + final Collection createdTargets = this.targetManagement .createTargets(MgmtTargetMapper.fromRequest(entityFactory, targets)); LOG.debug("{} targets created, return status {}", targets.size(), HttpStatus.CREATED); return new ResponseEntity<>(MgmtTargetMapper.toResponse(createdTargets), HttpStatus.CREATED); diff --git a/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeResourceTest.java b/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeResourceTest.java index 9d5b0d002..f3b19f5c8 100644 --- a/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeResourceTest.java +++ b/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeResourceTest.java @@ -26,6 +26,7 @@ import java.util.List; import org.apache.commons.lang3.RandomStringUtils; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; +import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType; import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; @@ -554,7 +555,7 @@ public class MgmtDistributionSetTypeResourceTest extends AbstractRestIntegration .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) .andExpect(status().isBadRequest()); - final DistributionSetType missingName = entityFactory.generateDistributionSetType("test123", null, "Desc123"); + final DistributionSetType missingName = new JpaDistributionSetType("test123", null, "Desc123"); mvc.perform(post("/rest/v1/distributionsettypes") .content(JsonBuilder.distributionSetTypes(Lists.newArrayList(missingName))) .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) diff --git a/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResourceTest.java b/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResourceTest.java index f5c79fb6c..f3f86fbc7 100644 --- a/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResourceTest.java +++ b/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResourceTest.java @@ -37,6 +37,7 @@ import org.eclipse.hawkbit.exception.SpServerError; import org.eclipse.hawkbit.mgmt.json.model.artifact.MgmtArtifact; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; +import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule; import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.LocalArtifact; @@ -457,7 +458,7 @@ public class MgmtSoftwareModuleResourceTest extends AbstractRestIntegrationTestW .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) .andExpect(status().isBadRequest()); - final SoftwareModule missingName = entityFactory.generateSoftwareModule(osType, null, "version 1", null, null); + final SoftwareModule missingName = new JpaSoftwareModule(osType, null, "version 1", null, null); mvc.perform( post("/rest/v1/softwaremodules").content(JsonBuilder.softwareModules(Lists.newArrayList(missingName))) .contentType(MediaType.APPLICATION_JSON)) diff --git a/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeResourceTest.java b/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeResourceTest.java index 8c60815e3..a0a5b397b 100644 --- a/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeResourceTest.java +++ b/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeResourceTest.java @@ -26,6 +26,7 @@ import java.util.List; import org.apache.commons.lang3.RandomStringUtils; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; +import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType; import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.test.util.WithUser; @@ -337,7 +338,7 @@ public class MgmtSoftwareModuleTypeResourceTest extends AbstractRestIntegrationT .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) .andExpect(status().isBadRequest()); - final SoftwareModuleType missingName = entityFactory.generateSoftwareModuleType("test123", null, "Desc123", 5); + final SoftwareModuleType missingName = new JpaSoftwareModuleType("test123", null, "Desc123", 5); mvc.perform(post("/rest/v1/softwaremoduletypes") .content(JsonBuilder.softwareModuleTypes(Lists.newArrayList(missingName))) .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/EntityFactory.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/EntityFactory.java index 87919729e..925351807 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/EntityFactory.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/EntityFactory.java @@ -11,6 +11,8 @@ package org.eclipse.hawkbit.repository; import java.util.Collection; import java.util.concurrent.TimeUnit; +import javax.validation.constraints.NotNull; + import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.ActionStatus; @@ -64,7 +66,7 @@ public interface EntityFactory { * * @return {@link ActionStatus} object */ - ActionStatus generateActionStatus(Action action, Status status, Long occurredAt); + ActionStatus generateActionStatus(@NotNull Action action, @NotNull Status status, Long occurredAt); /** * Generates an {@link ActionStatus} object without persisting it. @@ -81,7 +83,7 @@ public interface EntityFactory { * * @return {@link ActionStatus} object */ - ActionStatus generateActionStatus(Action action, final Status status, Long occurredAt, + ActionStatus generateActionStatus(@NotNull Action action, @NotNull Status status, Long occurredAt, final Collection messages); /** @@ -99,7 +101,8 @@ public interface EntityFactory { * * @return {@link ActionStatus} object */ - ActionStatus generateActionStatus(Action action, Status status, Long occurredAt, final String message); + ActionStatus generateActionStatus(@NotNull Action action, @NotNull Status status, Long occurredAt, + final String message); /** * Generates an empty {@link DistributionSet} without persisting it. @@ -124,8 +127,8 @@ public interface EntityFactory { * * @return {@link DistributionSet} object */ - DistributionSet generateDistributionSet(String name, String version, String description, DistributionSetType type, - Collection moduleList); + DistributionSet generateDistributionSet(@NotNull String name, @NotNull String version, String description, + @NotNull DistributionSetType type, Collection moduleList); /** * Generates an empty {@link DistributionSetMetadata} element without @@ -148,7 +151,8 @@ public interface EntityFactory { * * @return {@link DistributionSetMetadata} object */ - DistributionSetMetadata generateDistributionSetMetadata(DistributionSet distributionSet, String key, String value); + DistributionSetMetadata generateDistributionSetMetadata(@NotNull DistributionSet distributionSet, + @NotNull String key, String value); /** * Generates an empty {@link DistributionSetTag} without persisting it. @@ -164,7 +168,7 @@ public interface EntityFactory { * of the tag * @return {@link DistributionSetTag} object */ - DistributionSetTag generateDistributionSetTag(String name); + DistributionSetTag generateDistributionSetTag(@NotNull String name); /** * Generates a {@link DistributionSetTag} without persisting it. @@ -177,7 +181,7 @@ public interface EntityFactory { * of the tag * @return {@link DistributionSetTag} object */ - DistributionSetTag generateDistributionSetTag(String name, String description, String colour); + DistributionSetTag generateDistributionSetTag(@NotNull String name, String description, String colour); /** * Generates an empty {@link DistributionSetType} without persisting it. @@ -198,7 +202,7 @@ public interface EntityFactory { * * @return {@link DistributionSetType} object */ - DistributionSetType generateDistributionSetType(String key, String name, String description); + DistributionSetType generateDistributionSetType(@NotNull String key, @NotNull String name, String description); /** * Generates an empty {@link Rollout} without persisting it. @@ -237,8 +241,8 @@ public interface EntityFactory { * * @return {@link SoftwareModule} object */ - SoftwareModule generateSoftwareModule(SoftwareModuleType type, String name, String version, String description, - String vendor); + SoftwareModule generateSoftwareModule(@NotNull SoftwareModuleType type, @NotNull String name, + @NotNull String version, String description, String vendor); /** * Generates an empty {@link SoftwareModuleMetadata} pair without persisting @@ -260,7 +264,8 @@ public interface EntityFactory { * * @return {@link SoftwareModuleMetadata} object */ - SoftwareModuleMetadata generateSoftwareModuleMetadata(SoftwareModule softwareModule, String key, String value); + SoftwareModuleMetadata generateSoftwareModuleMetadata(@NotNull SoftwareModule softwareModule, @NotNull String key, + String value); /** * Generates an empty {@link SoftwareModuleType} without persisting it. @@ -283,7 +288,8 @@ public interface EntityFactory { * * @return {@link SoftwareModuleType} object */ - SoftwareModuleType generateSoftwareModuleType(String key, String name, String description, int maxAssignments); + SoftwareModuleType generateSoftwareModuleType(@NotNull String key, @NotNull String name, String description, + int maxAssignments); /** * Generates an empty {@link Target} without persisting it. @@ -307,7 +313,7 @@ public interface EntityFactory { * * @return {@link Target} object */ - Target generateTarget(@NotEmpty String controllerID, @NotEmpty String securityToken); + Target generateTarget(@NotEmpty String controllerID, String securityToken); /** * Generates an empty {@link TargetFilterQuery} without persisting it. @@ -330,7 +336,7 @@ public interface EntityFactory { * of the tag * @return {@link TargetTag} object */ - TargetTag generateTargetTag(String name); + TargetTag generateTargetTag(@NotNull String name); /** * Generates a {@link TargetTag} without persisting it. @@ -343,7 +349,7 @@ public interface EntityFactory { * of the tag * @return {@link TargetTag} object */ - TargetTag generateTargetTag(String name, String description, String colour); + TargetTag generateTargetTag(@NotNull String name, String description, String colour); /** * Generates an empty {@link LocalArtifact} without persisting it. diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/ActionStatus.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/ActionStatus.java index e83108fe8..d38bc4f75 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/ActionStatus.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/ActionStatus.java @@ -47,7 +47,7 @@ public interface ActionStatus extends TenantAwareBaseEntity { * @return current {@link Status#DOWNLOAD} progress if known by the update * server. */ - int getDownloadProgressPercent(); + short getDownloadProgressPercent(); /** * @return list of message entries that can be added to the diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Artifact.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Artifact.java index cc726839d..e0a142b43 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Artifact.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Artifact.java @@ -37,6 +37,6 @@ public interface Artifact extends TenantAwareBaseEntity { /** * @return size of the artifact in bytes. */ - Long getSize(); + long getSize(); } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/BaseEntity.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/BaseEntity.java index 2a9948bc5..c6717d021 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/BaseEntity.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/BaseEntity.java @@ -44,6 +44,6 @@ public interface BaseEntity extends Serializable, Identifiable { /** * @return version of the {@link BaseEntity}. */ - long getOptLockRevision(); + int getOptLockRevision(); } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/RolloutGroup.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/RolloutGroup.java index 3180fdb6a..286085eba 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/RolloutGroup.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/RolloutGroup.java @@ -155,7 +155,7 @@ public interface RolloutGroup extends NamedEntity { /** * @return the total amount of targets containing in this group */ - long getTotalTargets(); + int getTotalTargets(); /** * @return the totalTargetCountStatus diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetInfo.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetInfo.java index 2eb2f05c7..76a975f75 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetInfo.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetInfo.java @@ -36,7 +36,8 @@ public interface TargetInfo extends Serializable { /** * @return time in {@link TimeUnit#MILLISECONDS} GMT when the {@link Target} - * polled the server the last time. + * polled the server the last time or null if target + * has never queried yet. */ Long getLastTargetQuery(); diff --git a/hawkbit-repository/hawkbit-repository-jpa/pom.xml b/hawkbit-repository/hawkbit-repository-jpa/pom.xml index 3a66a0a9d..9f7a0bbb7 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/pom.xml +++ b/hawkbit-repository/hawkbit-repository-jpa/pom.xml @@ -89,6 +89,10 @@ cz.jirutka.rsql rsql-parser + + org.apache.commons + commons-collections4 + diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java index a62de9a06..f0e1a1301 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java @@ -8,7 +8,6 @@ */ package org.eclipse.hawkbit; -import java.util.HashMap; import java.util.Map; import org.eclipse.hawkbit.repository.ArtifactManagement; @@ -73,6 +72,7 @@ import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.validation.beanvalidation.MethodValidationPostProcessor; +import com.google.common.collect.Maps; import com.google.common.eventbus.EventBus; /** @@ -197,7 +197,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { @Override protected Map getVendorProperties() { - final Map properties = new HashMap<>(); + final Map properties = Maps.newHashMapWithExpectedSize(5); // Turn off dynamic weaving to disable LTW lookup in static weaving mode properties.put("eclipselink.weaving", "false"); // needed for reports diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/ActionRepository.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/ActionRepository.java index 8989a5a41..b39d760c3 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/ActionRepository.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/ActionRepository.java @@ -30,6 +30,7 @@ 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.domain.Sort; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; @@ -103,7 +104,7 @@ public interface ActionRepository extends BaseEntityRepository, * @return the found {@link Action} */ @EntityGraph(value = "Action.ds", type = EntityGraphType.LOAD) - Optional findFirstByTargetAndActiveOrderByIdAsc(final JpaTarget target, boolean active); + Optional findFirstByTargetAndActive(final Sort sort, final JpaTarget target, boolean active); /** * Retrieves latest {@link UpdateAction} for given target and diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java index 41ad6f179..c12838c9b 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java @@ -9,7 +9,7 @@ package org.eclipse.hawkbit.repository.jpa; import java.net.URI; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; @@ -54,6 +54,8 @@ import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Sort.Direction; import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.Modifying; import org.springframework.transaction.annotation.Isolation; @@ -163,12 +165,13 @@ public class JpaControllerManagement implements ControllerManagement { @Override public Optional findOldestActiveActionByTarget(final Target target) { - return actionRepository.findFirstByTargetAndActiveOrderByIdAsc((JpaTarget) target, true); + return actionRepository.findFirstByTargetAndActive(new Sort(Direction.ASC, "id"), (JpaTarget) target, true); } @Override public List findSoftwareModulesByDistributionSet(final DistributionSet distributionSet) { - return new ArrayList<>(softwareModuleRepository.findByAssignedTo((JpaDistributionSet) distributionSet)); + return Collections + .unmodifiableList(softwareModuleRepository.findByAssignedTo((JpaDistributionSet) distributionSet)); } @Override diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java index 5a2c6fdda..09c308a3b 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java @@ -593,7 +593,7 @@ public class JpaDeploymentManagement implements DeploymentManagement { multiselect.where(cb.equal(actionRoot.get(JpaAction_.target), target)); multiselect.orderBy(cb.desc(actionRoot.get(JpaAction_.id))); multiselect.groupBy(actionRoot.get(JpaAction_.id)); - return new ArrayList<>(entityManager.createQuery(multiselect).getResultList()); + return Collections.unmodifiableList(entityManager.createQuery(multiselect).getResultList()); } @Override diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetManagement.java index e170ccad7..a4295cdae 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetManagement.java @@ -126,26 +126,24 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { final DistributionSetTag myTag = tagManagement.findDistributionSetTag(tagName); DistributionSetTagAssignmentResult result; - final List toBeChangedDSs = new ArrayList<>(); - for (final JpaDistributionSet set : sets) { - if (set.getTags().add(myTag)) { - toBeChangedDSs.add(set); - } - } + + final List toBeChangedDSs = sets.stream().filter(set -> set.addTag(myTag)) + .collect(Collectors.toList()); // un-assignment case if (toBeChangedDSs.isEmpty()) { for (final JpaDistributionSet set : sets) { - if (set.getTags().remove(myTag)) { + if (set.removeTag(myTag)) { toBeChangedDSs.add(set); } } result = new DistributionSetTagAssignmentResult(dsIds.size() - toBeChangedDSs.size(), 0, toBeChangedDSs.size(), Collections.emptyList(), - new ArrayList<>(distributionSetRepository.save(toBeChangedDSs)), myTag); + Collections.unmodifiableList(distributionSetRepository.save(toBeChangedDSs)), myTag); } else { result = new DistributionSetTagAssignmentResult(dsIds.size() - toBeChangedDSs.size(), toBeChangedDSs.size(), - 0, new ArrayList<>(distributionSetRepository.save(toBeChangedDSs)), Collections.emptyList(), myTag); + 0, Collections.unmodifiableList(distributionSetRepository.save(toBeChangedDSs)), + Collections.emptyList(), myTag); } final DistributionSetTagAssignmentResult resultAssignment = result; @@ -174,8 +172,6 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { @Modifying @Transactional(isolation = Isolation.READ_UNCOMMITTED) public void deleteDistributionSet(final Long... distributionSetIDs) { - final List toHardDelete = new ArrayList<>(); - final List assigned = distributionSetRepository .findAssignedToTargetDistributionSetsById(distributionSetIDs); assigned.addAll(distributionSetRepository.findAssignedToRolloutDistributionSetsById(distributionSetIDs)); @@ -186,13 +182,10 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { } // mark the rest as hard delete - for (final Long setId : distributionSetIDs) { - if (!assigned.contains(setId)) { - toHardDelete.add(setId); - } - } + final List toHardDelete = Arrays.stream(distributionSetIDs).filter(setId -> !assigned.contains(setId)) + .collect(Collectors.toList()); - // hard delete the rest if exixts + // hard delete the rest if exists if (!toHardDelete.isEmpty()) { // don't give the delete statement an empty list, JPA/Oracle cannot // handle the empty list @@ -239,7 +232,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { @SuppressWarnings({ "unchecked", "rawtypes" }) final Collection toSave = (Collection) distributionSets; - return new ArrayList<>(distributionSetRepository.save(toSave)); + return Collections.unmodifiableList(distributionSetRepository.save(toSave)); } @Override @@ -292,7 +285,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { } private static Page convertDsTPage(final Page findAll) { - return new PageImpl<>(new ArrayList<>(findAll.getContent())); + return new PageImpl<>(Collections.unmodifiableList(findAll.getContent())); } @Override @@ -310,7 +303,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { private static Page convertDsPage(final Page findAll, final Pageable pageable) { - return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements()); + return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, findAll.getTotalElements()); } /** @@ -333,7 +326,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { @Override public Page findDistributionSetsByDeletedAndOrCompleted(final Pageable pageReq, final Boolean deleted, final Boolean complete) { - final List> specList = new ArrayList<>(); + final List> specList = new ArrayList<>(2); if (deleted != null) { final Specification spec = DistributionSetSpecification.isDeleted(deleted); @@ -354,11 +347,12 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { final Specification spec = RSQLUtility.parse(rsqlParam, DistributionSetFields.class); - final List> specList = new ArrayList<>(); + final List> specList = new LinkedList<>(); if (deleted != null) { specList.add(DistributionSetSpecification.isDeleted(deleted)); } specList.add(spec); + return convertDsPage(findByCriteriaAPI(pageReq, specList), pageReq); } @@ -414,7 +408,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { @Override public List findDistributionSetsAll(final Collection dist) { - return new ArrayList<>(distributionSetRepository.findAll(DistributionSetSpecification.byIds(dist))); + return Collections + .unmodifiableList(distributionSetRepository.findAll(DistributionSetSpecification.byIds(dist))); } @Override @@ -555,7 +550,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { @Override public List findDistributionSetMetadataByDistributionSetId(final Long distributionSetId) { - return new ArrayList<>(distributionSetMetadataRepository + return Collections.unmodifiableList(distributionSetMetadataRepository .findAll((Specification) (root, query, cb) -> cb.equal( root.get(JpaDistributionSetMetadata_.distributionSet).get(JpaDistributionSet_.id), distributionSetId))); @@ -579,7 +574,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { private static Page convertMdPage(final Page findAll, final Pageable pageable) { - return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements()); + return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, findAll.getTotalElements()); } @Override @@ -604,7 +599,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { private static List> buildDistributionSetSpecifications( final DistributionSetFilter distributionSetFilter) { - final List> specList = new ArrayList<>(); + final List> specList = new LinkedList<>(); Specification spec; @@ -705,9 +700,9 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { public List assignTag(final Collection dsIds, final DistributionSetTag tag) { final List allDs = findDistributionSetListWithDetails(dsIds); - allDs.forEach(ds -> ds.getTags().add(tag)); + allDs.forEach(ds -> ds.addTag(tag)); - final List save = new ArrayList<>(distributionSetRepository.save(allDs)); + final List save = Collections.unmodifiableList(distributionSetRepository.save(allDs)); afterCommit.afterCommit(() -> { @@ -727,7 +722,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { @SuppressWarnings({ "unchecked", "rawtypes" }) final Collection distributionSets = (Collection) tag.getAssignedToDistributionSet(); - return new ArrayList<>(unAssignTag(distributionSets, tag)); + return Collections.unmodifiableList(unAssignTag(distributionSets, tag)); } @Override @@ -741,7 +736,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { private List unAssignTag(final Collection distributionSets, final DistributionSetTag tag) { - distributionSets.forEach(ds -> ds.getTags().remove(tag)); + distributionSets.forEach(ds -> ds.removeTag(tag)); return distributionSetRepository.save(distributionSets); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaEntityFactory.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaEntityFactory.java index 51b59f08f..1bc8063ec 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaEntityFactory.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaEntityFactory.java @@ -43,11 +43,13 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetFilterQuery; import org.eclipse.hawkbit.repository.model.TargetTag; +import org.springframework.validation.annotation.Validated; /** * JPA Implementation of {@link EntityFactory}. * */ +@Validated public class JpaEntityFactory implements EntityFactory { @Override diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutGroupManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutGroupManagement.java index 9f63204b1..64d81cb70 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutGroupManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutGroupManagement.java @@ -8,7 +8,7 @@ */ package org.eclipse.hawkbit.repository.jpa; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -81,11 +81,11 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement { } private static Page convertPage(final Page findAll, final Pageable pageable) { - return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements()); + return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, findAll.getTotalElements()); } private static Page convertTPage(final Page findAll, final Pageable pageable) { - return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements()); + return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, findAll.getTotalElements()); } @Override @@ -114,7 +114,7 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement { for (final RolloutGroup rolloutGroup : rolloutGroups) { final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus( - allStatesForRollout.get(rolloutGroup.getId()), rolloutGroup.getTotalTargets()); + allStatesForRollout.get(rolloutGroup.getId()), new Long(rolloutGroup.getTotalTargets())); rolloutGroup.setTotalTargetCountStatus(totalTargetCountStatus); } @@ -128,7 +128,7 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement { .getStatusCountByRolloutGroupId(rolloutGroupId); final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(rolloutStatusCountItems, - rolloutGroup.getTotalTargets()); + new Long(rolloutGroup.getTotalTargets())); rolloutGroup.setTotalTargetCountStatus(totalTargetCountStatus); return rolloutGroup; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutManagement.java index 3a903ea89..1cb763f3a 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutManagement.java @@ -8,7 +8,7 @@ */ package org.eclipse.hawkbit.repository.jpa; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; @@ -140,11 +140,11 @@ public class JpaRolloutManagement implements RolloutManagement { } private static Page convertPage(final Page findAll, final Pageable pageable) { - return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements()); + return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, findAll.getTotalElements()); } private static Slice convertPage(final Slice findAll, final Pageable pageable) { - return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, 0); + return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, 0); } @Override @@ -651,7 +651,7 @@ public class JpaRolloutManagement implements RolloutManagement { @Override public float getFinishedPercentForRunningGroup(final Long rolloutId, final RolloutGroup rolloutGroup) { - final Long totalGroup = rolloutGroup.getTotalTargets(); + final int totalGroup = rolloutGroup.getTotalTargets(); final Long finished = actionRepository.countByRolloutIdAndRolloutGroupIdAndStatus(rolloutId, rolloutGroup.getId(), Action.Status.FINISHED); if (totalGroup == 0) { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareManagement.java index 3f8c1a54b..62ebfcb69 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareManagement.java @@ -11,12 +11,15 @@ package org.eclipse.hawkbit.repository.jpa; import static com.google.common.base.Preconditions.checkNotNull; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.persistence.EntityManager; import javax.persistence.criteria.CriteriaBuilder; @@ -65,6 +68,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import com.google.common.base.Strings; +import com.google.common.collect.Lists; import com.google.common.collect.Sets; /** @@ -166,7 +170,7 @@ public class JpaSoftwareManagement implements SoftwareManagement { @SuppressWarnings({ "unchecked", "rawtypes" }) final Collection jpaCast = (Collection) swModules; - return new ArrayList<>(softwareModuleRepository.save(jpaCast)); + return Collections.unmodifiableList(softwareModuleRepository.save(jpaCast)); } @Override @@ -185,22 +189,22 @@ public class JpaSoftwareManagement implements SoftwareManagement { private static Slice convertSmPage(final Slice findAll, final Pageable pageable) { - return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, 0); + return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, 0); } private static Page convertSmPage(final Page findAll, final Pageable pageable) { - return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements()); + return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, findAll.getTotalElements()); } private static Page convertSmMdPage(final Page findAll, final Pageable pageable) { - return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements()); + return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, findAll.getTotalElements()); } @Override public Long countSoftwareModulesByType(final SoftwareModuleType type) { - final List> specList = new ArrayList<>(); + final List> specList = new LinkedList<>(); Specification spec = SoftwareModuleSpecification.equalType((JpaSoftwareModuleType) type); specList.add(spec); @@ -277,7 +281,7 @@ public class JpaSoftwareManagement implements SoftwareManagement { @Override public Slice findSoftwareModulesAll(final Pageable pageable) { - final List> specList = new ArrayList<>(); + final List> specList = new ArrayList<>(2); Specification spec = SoftwareModuleSpecification.isDeletedFalse(); specList.add(spec); @@ -296,13 +300,9 @@ public class JpaSoftwareManagement implements SoftwareManagement { @Override public Long countSoftwareModulesAll() { - - final List> specList = new ArrayList<>(); - final Specification spec = SoftwareModuleSpecification.isDeletedFalse(); - specList.add(spec); - return countSwModuleByCriteriaAPI(specList); + return countSwModuleByCriteriaAPI(Lists.newArrayList(spec)); } @Override @@ -327,20 +327,20 @@ public class JpaSoftwareManagement implements SoftwareManagement { private static Page convertSmTPage(final Page findAll, final Pageable pageable) { - return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements()); + return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, findAll.getTotalElements()); } @Override @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) public List findSoftwareModulesById(final Collection ids) { - return new ArrayList<>(softwareModuleRepository.findByIdIn(ids)); + return Collections.unmodifiableList(softwareModuleRepository.findByIdIn(ids)); } @Override public Slice findSoftwareModuleByFilters(final Pageable pageable, final String searchText, final SoftwareModuleType type) { - final List> specList = new ArrayList<>(); + final List> specList = new ArrayList<>(4); Specification spec = SoftwareModuleSpecification.isDeletedFalse(); specList.add(spec); @@ -438,7 +438,7 @@ public class JpaSoftwareManagement implements SoftwareManagement { private static List> buildSpecificationList(final String searchText, final JpaSoftwareModuleType type) { - final List> specList = new ArrayList<>(); + final List> specList = new ArrayList<>(3); if (!Strings.isNullOrEmpty(searchText)) { specList.add(SoftwareModuleSpecification.likeNameOrVersion(searchText)); } @@ -452,18 +452,15 @@ public class JpaSoftwareManagement implements SoftwareManagement { private Predicate[] specificationsToPredicate(final List> specifications, final Root root, final CriteriaQuery query, final CriteriaBuilder cb, final Predicate... additionalPredicates) { - final List 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()]); + + return Stream.concat(specifications.stream().map(spec -> spec.toPredicate(root, query, cb)), + Arrays.stream(additionalPredicates)).toArray(Predicate[]::new); } @Override public Long countSoftwareModuleByFilters(final String searchText, final SoftwareModuleType type) { - final List> specList = new ArrayList<>(); + final List> specList = new ArrayList<>(3); Specification spec = SoftwareModuleSpecification.isDeletedFalse(); specList.add(spec); @@ -573,7 +570,7 @@ public class JpaSoftwareManagement implements SoftwareManagement { checkAndThrowAlreadyExistsIfSoftwareModuleMetadataExists(softwareModuleMetadata.getId()); } metadata.forEach(m -> entityManager.merge((JpaSoftwareModule) m.getSoftwareModule()).setLastModifiedAt(-1L)); - return new ArrayList<>(softwareModuleMetadataRepository.save(metadata)); + return Collections.unmodifiableList(softwareModuleMetadataRepository.save(metadata)); } @Override @@ -623,8 +620,8 @@ public class JpaSoftwareManagement implements SoftwareManagement { @Override public List findSoftwareModuleMetadataBySoftwareModuleId(final Long softwareModuleId) { - return new ArrayList<>( - softwareModuleMetadataRepository + return Collections + .unmodifiableList(softwareModuleMetadataRepository .findAll((Specification) (root, query, cb) -> cb.and(cb.equal( root.get(JpaSoftwareModuleMetadata_.softwareModule).get(JpaSoftwareModule_.id), diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTagManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTagManagement.java index e881d8ef8..fb4b1999f 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTagManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTagManagement.java @@ -10,8 +10,8 @@ package org.eclipse.hawkbit.repository.jpa; import static com.google.common.base.Preconditions.checkNotNull; -import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -111,7 +111,7 @@ public class JpaTagManagement implements TagManagement { } }); - final List save = new ArrayList<>(targetTagRepository.save(targetTags)); + final List save = Collections.unmodifiableList(targetTagRepository.save(targetTags)); afterCommit .afterCommit(() -> eventBus.post(new TargetTagCreatedBulkEvent(tenantAware.getCurrentTenant(), save))); return save; @@ -141,7 +141,7 @@ public class JpaTagManagement implements TagManagement { @Override public List findAllTargetTags() { - return new ArrayList<>(targetTagRepository.findAll()); + return Collections.unmodifiableList(targetTagRepository.findAll()); } @Override @@ -152,12 +152,12 @@ public class JpaTagManagement implements TagManagement { } private static Page convertTPage(final Page findAll, final Pageable pageable) { - return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements()); + return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, findAll.getTotalElements()); } private static Page convertDsPage(final Page findAll, final Pageable pageable) { - return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements()); + return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, findAll.getTotalElements()); } @Override @@ -213,7 +213,8 @@ public class JpaTagManagement implements TagManagement { throw new EntityAlreadyExistsException(); } } - final List save = new ArrayList<>(distributionSetTagRepository.save(distributionSetTags)); + final List save = Collections + .unmodifiableList(distributionSetTagRepository.save(distributionSetTags)); afterCommit.afterCommit( () -> eventBus.post(new DistributionSetTagCreatedBulkEvent(tenantAware.getCurrentTenant(), save))); @@ -228,7 +229,7 @@ public class JpaTagManagement implements TagManagement { final List changed = new LinkedList<>(); for (final JpaDistributionSet set : distributionSetRepository.findByTag(tag)) { - set.getTags().remove(tag); + set.removeTag(tag); changed.add(set); } @@ -254,7 +255,7 @@ public class JpaTagManagement implements TagManagement { @Override public List findAllDistributionSetTags() { - return new ArrayList<>(distributionSetTagRepository.findAll()); + return Collections.unmodifiableList(distributionSetTagRepository.findAll()); } @Override diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java index 225c0b4b2..67c28216d 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java @@ -13,6 +13,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.LinkedList; import java.util.List; import java.util.stream.Collectors; @@ -127,7 +128,7 @@ public class JpaTargetManagement implements TargetManagement { @Override public List findTargetByControllerID(final Collection controllerIDs) { - return new ArrayList<>(targetRepository + return Collections.unmodifiableList(targetRepository .findAll(TargetSpecifications.byControllerIdWithStatusAndAssignedInJoin(controllerIDs))); } @@ -193,7 +194,7 @@ public class JpaTargetManagement implements TargetManagement { toUpdate.forEach(target -> target.setNew(false)); - return new ArrayList<>(targetRepository.save(toUpdate)); + return Collections.unmodifiableList(targetRepository.save(toUpdate)); } @Override @@ -236,11 +237,11 @@ public class JpaTargetManagement implements TargetManagement { } private static Page convertPage(final Page findAll, final Pageable pageable) { - return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements()); + return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, findAll.getTotalElements()); } private static Slice convertPage(final Slice findAll, final Pageable pageable) { - return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, 0); + return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, 0); } @Override @@ -289,7 +290,7 @@ public class JpaTargetManagement implements TargetManagement { private static List> buildSpecificationList(final Collection status, final String searchText, final Long installedOrAssignedDistributionSetId, final Boolean selectTargetWithNoTag, final boolean fetch, final String... tagNames) { - final List> specList = new ArrayList<>(); + final List> specList = new LinkedList<>(); if (status != null && !status.isEmpty()) { specList.add(TargetSpecifications.hasTargetUpdateStatus(status, fetch)); } @@ -342,7 +343,7 @@ public class JpaTargetManagement implements TargetManagement { // all are already assigned -> unassign if (alreadyAssignedTargets.size() == allTargets.size()) { - alreadyAssignedTargets.forEach(target -> target.getTags().remove(tag)); + alreadyAssignedTargets.forEach(target -> ((JpaTarget) target).removeTag(tag)); final TargetTagAssignmentResult result = new TargetTagAssignmentResult(0, 0, alreadyAssignedTargets.size(), Collections.emptyList(), alreadyAssignedTargets, tag); @@ -352,9 +353,10 @@ public class JpaTargetManagement implements TargetManagement { allTargets.removeAll(alreadyAssignedTargets); // some or none are assigned -> assign - allTargets.forEach(target -> target.getTags().add(tag)); + allTargets.forEach(target -> target.addTag(tag)); final TargetTagAssignmentResult result = new TargetTagAssignmentResult(alreadyAssignedTargets.size(), - allTargets.size(), 0, new ArrayList<>(targetRepository.save(allTargets)), Collections.emptyList(), tag); + allTargets.size(), 0, Collections.unmodifiableList(targetRepository.save(allTargets)), + Collections.emptyList(), tag); afterCommit.afterCommit(() -> eventBus.post(new TargetTagAssigmentResultEvent(result))); @@ -370,8 +372,8 @@ public class JpaTargetManagement implements TargetManagement { final List allTargets = targetRepository .findAll(TargetSpecifications.byControllerIdWithStatusAndTagsInJoin(targetIds)); - allTargets.forEach(target -> target.getTags().add(tag)); - final List save = new ArrayList<>(targetRepository.save(allTargets)); + allTargets.forEach(target -> target.addTag(tag)); + final List save = Collections.unmodifiableList(targetRepository.save(allTargets)); afterCommit.afterCommit(() -> { final TargetTagAssignmentResult assigmentResult = new TargetTagAssignmentResult(0, save.size(), 0, save, @@ -385,9 +387,9 @@ public class JpaTargetManagement implements TargetManagement { private List unAssignTag(final Collection targets, final TargetTag tag) { final Collection toUnassign = (Collection) targets; - toUnassign.forEach(target -> target.getTags().remove(tag)); + toUnassign.forEach(target -> target.removeTag(tag)); - final List save = new ArrayList<>(targetRepository.save(toUnassign)); + final List save = Collections.unmodifiableList(targetRepository.save(toUnassign)); afterCommit.afterCommit(() -> { final TargetTagAssignmentResult assigmentResult = new TargetTagAssignmentResult(0, 0, save.size(), Collections.emptyList(), save, tag); @@ -407,7 +409,7 @@ public class JpaTargetManagement implements TargetManagement { @Modifying @Transactional(isolation = Isolation.READ_UNCOMMITTED) public Target unAssignTag(final String controllerID, final TargetTag targetTag) { - final List allTargets = new ArrayList<>(targetRepository + final List allTargets = Collections.unmodifiableList(targetRepository .findAll(TargetSpecifications.byControllerIdWithStatusAndTagsInJoin(Arrays.asList(controllerID)))); final List unAssignTag = unAssignTag(allTargets, targetTag); return unAssignTag.isEmpty() ? null : unAssignTag.get(0); @@ -463,7 +465,7 @@ public class JpaTargetManagement implements TargetManagement { final List resultList = entityManager.createQuery(query).setFirstResult(pageable.getOffset()) .setMaxResults(pageSize + 1).getResultList(); final boolean hasNext = resultList.size() > pageSize; - return new SliceImpl<>(new ArrayList<>(resultList), pageable, hasNext); + return new SliceImpl<>(Collections.unmodifiableList(resultList), pageable, hasNext); } private static Predicate[] specificationsToPredicate(final List> specifications, @@ -545,11 +547,8 @@ public class JpaTargetManagement implements TargetManagement { targetRoot.get(JpaTarget_.controllerId), targetRoot.get(JpaTarget_.name), targetRoot.get(sortProperty)); final Specification spec = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class); - final List> specList = new ArrayList<>(); - specList.add(spec); - - final Predicate[] specificationsForMultiSelect = specificationsToPredicate(specList, targetRoot, multiselect, - cb); + final Predicate[] specificationsForMultiSelect = specificationsToPredicate(Lists.newArrayList(spec), targetRoot, + multiselect, cb); // if we have some predicates then add it to the where clause of the // multiselect @@ -611,18 +610,16 @@ public class JpaTargetManagement implements TargetManagement { targets.stream().map(target -> target.getControllerId()).collect(Collectors.toList())) > 0) { throw new EntityAlreadyExistsException(); } - final List savedTargets = new ArrayList<>(); - for (final Target t : targets) { - final Target myTarget = createTarget(t, TargetUpdateStatus.UNKNOWN, null, t.getTargetInfo().getAddress()); - savedTargets.add(myTarget); - } - return savedTargets; + + return targets.stream() + .map(t -> createTarget(t, TargetUpdateStatus.UNKNOWN, null, t.getTargetInfo().getAddress())) + .collect(Collectors.toList()); } @Override public List findTargetsByTag(final String tagName) { final JpaTargetTag tag = targetTagRepository.findByNameEquals(tagName); - return new ArrayList<>(targetRepository.findByTag(tag)); + return Collections.unmodifiableList(targetRepository.findByTag(tag)); } @Override diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/aspects/ExceptionMappingAspectHandler.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/aspects/ExceptionMappingAspectHandler.java index c18f614fd..5dfb70633 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/aspects/ExceptionMappingAspectHandler.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/aspects/ExceptionMappingAspectHandler.java @@ -10,7 +10,6 @@ package org.eclipse.hawkbit.repository.jpa.aspects; import java.sql.SQLException; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -36,6 +35,8 @@ import org.springframework.orm.jpa.JpaVendorAdapter; import org.springframework.security.access.AccessDeniedException; import org.springframework.transaction.TransactionSystemException; +import com.google.common.collect.Maps; + /** * {@link Aspect} catches persistence exceptions and wraps them to custom * specific exceptions Additionally it checks and prevents access to certain @@ -49,14 +50,14 @@ public class ExceptionMappingAspectHandler implements Ordered { private static final Logger LOG = LoggerFactory.getLogger(ExceptionMappingAspectHandler.class); - private static final Map EXCEPTION_MAPPING = new HashMap<>(); + private static final Map EXCEPTION_MAPPING = Maps.newHashMapWithExpectedSize(4); /** * 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> MAPPED_EXCEPTION_ORDER = new ArrayList<>(); + private static final List> MAPPED_EXCEPTION_ORDER = new ArrayList<>(4); @Autowired private JpaVendorAdapter jpaVendorAdapter; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaArtifact.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaArtifact.java index 6ead1df9e..1cf6921a4 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaArtifact.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaArtifact.java @@ -32,7 +32,7 @@ public abstract class AbstractJpaArtifact extends AbstractJpaTenantAwareBaseEnti private String md5Hash; @Column(name = "file_size") - private Long size; + private long size; @Override public abstract SoftwareModule getSoftwareModule(); @@ -56,11 +56,11 @@ public abstract class AbstractJpaArtifact extends AbstractJpaTenantAwareBaseEnti } @Override - public Long getSize() { + public long getSize() { return size; } - public void setSize(final Long size) { + public void setSize(final long size) { this.size = size; } } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaBaseEntity.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaBaseEntity.java index ef1868c3f..983cb2763 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaBaseEntity.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaBaseEntity.java @@ -48,7 +48,7 @@ public abstract class AbstractJpaBaseEntity implements BaseEntity { @Version @Column(name = "optlock_revision") - private long optLockRevision; + private int optLockRevision; /** * Default constructor needed for JPA entities. @@ -106,11 +106,11 @@ public abstract class AbstractJpaBaseEntity implements BaseEntity { } @Override - public long getOptLockRevision() { + public int getOptLockRevision() { return optLockRevision; } - public void setOptLockRevision(final long optLockRevision) { + public void setOptLockRevision(final int optLockRevision) { this.optLockRevision = optLockRevision; } @@ -142,7 +142,7 @@ public abstract class AbstractJpaBaseEntity implements BaseEntity { final int prime = 31; int result = 1; result = prime * result + (id == null ? 0 : id.hashCode()); - result = prime * result + (int) (optLockRevision ^ optLockRevision >>> 32); + result = prime * result + optLockRevision; result = prime * result + this.getClass().getName().hashCode(); return result; } @@ -179,5 +179,4 @@ public abstract class AbstractJpaBaseEntity implements BaseEntity { return true; } - } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaMetaData.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaMetaData.java index a9f93cb86..c6df089f7 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaMetaData.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaMetaData.java @@ -26,7 +26,7 @@ public abstract class AbstractJpaMetaData implements MetaData { private static final long serialVersionUID = 1L; @Id - @Column(name = "meta_key", length = 128) + @Column(name = "meta_key", nullable = false, length = 128) @Size(min = 1, max = 128) @NotNull private String key; @@ -37,7 +37,6 @@ public abstract class AbstractJpaMetaData implements MetaData { private String value; public AbstractJpaMetaData(final String key, final String value) { - super(); this.key = key; this.value = value; } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaNamedEntity.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaNamedEntity.java index 0011cc2da..634d4490f 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaNamedEntity.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaNamedEntity.java @@ -40,7 +40,7 @@ public abstract class AbstractJpaNamedEntity extends AbstractJpaTenantAwareBaseE * Default constructor. */ public AbstractJpaNamedEntity() { - super(); + // Default constructor needed for JPA entities } /** diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaNamedVersionedEntity.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaNamedVersionedEntity.java index 9c78f976c..9e45f3a76 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaNamedVersionedEntity.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaNamedVersionedEntity.java @@ -47,7 +47,7 @@ public abstract class AbstractJpaNamedVersionedEntity extends AbstractJpaNamedEn } AbstractJpaNamedVersionedEntity() { - super(); + // Default constructor needed for JPA entities } @Override diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaTag.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaTag.java index 12aad8d6d..f759d16fc 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaTag.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaTag.java @@ -31,7 +31,7 @@ public abstract class AbstractJpaTag extends AbstractJpaNamedEntity implements T private String colour; protected AbstractJpaTag() { - super(); + // Default constructor needed for JPA entities } /** diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaAction.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaAction.java index 4b5b6567e..ee3e17b8b 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaAction.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaAction.java @@ -8,6 +8,7 @@ */ package org.eclipse.hawkbit.repository.jpa.model; +import java.util.Collections; import java.util.List; import javax.persistence.CascadeType; @@ -134,7 +135,11 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio @Override public List getActionStatus() { - return actionStatus; + if (actionStatus == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(actionStatus); } @Override diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaActionStatus.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaActionStatus.java index 102b25952..f442f552a 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaActionStatus.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaActionStatus.java @@ -8,7 +8,8 @@ */ package org.eclipse.hawkbit.repository.jpa.model; -import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; import java.util.List; import javax.persistence.CollectionTable; @@ -67,14 +68,14 @@ public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements @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 messages = new ArrayList<>(); + private List messages; /** * Note: filled only in {@link Status#DOWNLOAD}. */ @Transient @CacheField(key = CacheKeys.DOWNLOAD_PROGRESS_PERCENT) - private int downloadProgressPercent; + private short downloadProgressPercent; /** * Creates a new {@link ActionStatus} object. @@ -86,7 +87,7 @@ public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements * @param occurredAt * the occurred timestamp */ - public JpaActionStatus(final Action action, final Status status, final Long occurredAt) { + public JpaActionStatus(final Action action, final Status status, final long occurredAt) { this.action = (JpaAction) action; this.status = status; this.occurredAt = occurredAt; @@ -119,7 +120,7 @@ public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements } @Override - public int getDownloadProgressPercent() { + public short getDownloadProgressPercent() { return downloadProgressPercent; } @@ -135,6 +136,10 @@ public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements @Override public final void addMessage(final String message) { + if (messages == null) { + messages = new LinkedList<>(); + } + if (message != null) { Splitter.fixedLength(512).split(message).forEach(messages::add); } @@ -142,7 +147,11 @@ public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements @Override public List getMessages() { - return messages; + if (messages == null) { + messages = Collections.emptyList(); + } + + return Collections.unmodifiableList(messages); } @Override diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaActionWithStatusCount.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaActionWithStatusCount.java index 32b01cbe2..648ba344b 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaActionWithStatusCount.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaActionWithStatusCount.java @@ -59,7 +59,7 @@ public class JpaActionWithStatusCount implements ActionWithStatusCount { // Exception squid:S00107 - needed this way for JPA to fill the view @SuppressWarnings("squid:S00107") public JpaActionWithStatusCount(final Long actionId, final ActionType actionType, final boolean active, - final long forcedTime, final Status status, final Long actionCreatedAt, final Long actionLastModifiedAt, + final Long forcedTime, final Status status, final Long actionCreatedAt, final Long actionLastModifiedAt, final Long dsId, final String dsName, final String dsVersion, final Long actionStatusCount, final String rolloutName) { this.dsId = dsId; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSet.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSet.java index 9fc27f1e8..349d8f7e7 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSet.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSet.java @@ -8,7 +8,6 @@ */ package org.eclipse.hawkbit.repository.jpa.model; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -82,13 +81,13 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen @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 modules = new HashSet<>(); + private Set modules; @ManyToMany(targetEntity = JpaDistributionSetTag.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 tags = new HashSet<>(); + private Set tags; @Column(name = "deleted") private boolean deleted; @@ -106,7 +105,7 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen @OneToMany(fetch = FetchType.LAZY, targetEntity = JpaDistributionSetMetadata.class, cascade = { CascadeType.REMOVE }) @JoinColumn(name = "ds_id", insertable = false, updatable = false) - private final List metadata = new ArrayList<>(); + private List metadata; @ManyToOne(fetch = FetchType.LAZY, targetEntity = JpaDistributionSetType.class) @JoinColumn(name = "ds_id", nullable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstype_ds")) @@ -152,7 +151,27 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen @Override public Set getTags() { - return tags; + if (tags == null) { + return Collections.emptySet(); + } + + return Collections.unmodifiableSet(tags); + } + + public boolean addTag(final DistributionSetTag tag) { + if (tags == null) { + tags = new HashSet<>(); + } + + return tags.add(tag); + } + + public boolean removeTag(final DistributionSetTag tag) { + if (tags == null) { + return false; + } + + return tags.remove(tag); } @Override @@ -162,11 +181,19 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen @Override public List getMetadata() { + if (metadata == null) { + return Collections.emptyList(); + } + return Collections.unmodifiableList(metadata); } public List getActions() { - return actions; + if (actions == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(actions); } @Override @@ -186,19 +213,22 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen return this; } - public DistributionSet setTags(final Set tags) { - this.tags = tags; - return this; - } - @Override public List getAssignedTargets() { - return assignedToTargets; + if (assignedToTargets == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(assignedToTargets); } @Override public List getInstalledTargets() { - return installedAtTargets; + if (installedAtTargets == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(installedAtTargets); } @Override @@ -209,11 +239,18 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen @Override public Set getModules() { + if (modules == null) { + return Collections.emptySet(); + } + return Collections.unmodifiableSet(modules); } @Override public boolean addModule(final SoftwareModule softwareModule) { + if (modules == null) { + modules = new HashSet<>(); + } // we cannot allow that modules are added without a type defined if (type == null) { @@ -251,6 +288,10 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen @Override public boolean removeModule(final SoftwareModule softwareModule) { + if (modules == null) { + return false; + } + final Optional found = modules.stream() .filter(module -> module.getId().equals(softwareModule.getId())).findFirst(); @@ -266,6 +307,10 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen @Override public SoftwareModule findFirstModuleByType(final SoftwareModuleType type) { + if (modules == null) { + return null; + } + final Optional result = modules.stream().filter(module -> module.getType().equals(type)) .findFirst(); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSetTag.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSetTag.java index 1663fb620..84818685f 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSetTag.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSetTag.java @@ -8,6 +8,7 @@ */ package org.eclipse.hawkbit.repository.jpa.model; +import java.util.Collections; import java.util.List; import javax.persistence.Entity; @@ -68,7 +69,11 @@ public class JpaDistributionSetTag extends AbstractJpaTag implements Distributio @Override public List getAssignedToDistributionSet() { - return assignedToDistributionSet; + if (assignedToDistributionSet == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(assignedToDistributionSet); } @Override diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSetType.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSetType.java index 2a696aac9..e7bc9d347 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSetType.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSetType.java @@ -8,6 +8,7 @@ */ package org.eclipse.hawkbit.repository.jpa.model; +import java.util.Collections; import java.util.HashSet; import java.util.Optional; import java.util.Set; @@ -24,6 +25,7 @@ import javax.persistence.Table; import javax.persistence.UniqueConstraint; import javax.validation.constraints.Size; +import org.apache.commons.collections4.CollectionUtils; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; @@ -48,7 +50,7 @@ public class JpaDistributionSetType extends AbstractJpaNamedEntity implements Di @OneToMany(targetEntity = DistributionSetTypeElement.class, cascade = { CascadeType.ALL }, fetch = FetchType.EAGER, orphanRemoval = true) @JoinColumn(name = "distribution_set_type", insertable = false, updatable = false) - private final Set elements = new HashSet<>(); + private Set elements; @Column(name = "type_key", nullable = false, length = 64) @Size(max = 64) @@ -108,23 +110,44 @@ public class JpaDistributionSetType extends AbstractJpaNamedEntity implements Di @Override public Set getMandatoryModuleTypes() { + if (elements == null) { + return Collections.emptySet(); + } + return elements.stream().filter(element -> element.isMandatory()).map(element -> element.getSmType()) .collect(Collectors.toSet()); } @Override public Set getOptionalModuleTypes() { + if (elements == null) { + return Collections.emptySet(); + } + return elements.stream().filter(element -> !element.isMandatory()).map(element -> element.getSmType()) .collect(Collectors.toSet()); } @Override public boolean areModuleEntriesIdentical(final DistributionSetType dsType) { + if (!(dsType instanceof JpaDistributionSetType)) { + return false; + } else if (CollectionUtils.isEmpty(elements) + && CollectionUtils.isEmpty(((JpaDistributionSetType) dsType).elements)) { + return true; + } else if (CollectionUtils.isEmpty(elements)) { + return false; + } + return new HashSet(((JpaDistributionSetType) dsType).elements).equals(elements); } @Override public DistributionSetType addOptionalModuleType(final SoftwareModuleType smType) { + if (elements == null) { + elements = new HashSet<>(); + } + elements.add(new DistributionSetTypeElement(this, (JpaSoftwareModuleType) smType, false)); return this; @@ -132,6 +155,10 @@ public class JpaDistributionSetType extends AbstractJpaNamedEntity implements Di @Override public DistributionSetType addMandatoryModuleType(final SoftwareModuleType smType) { + if (elements == null) { + elements = new HashSet<>(); + } + elements.add(new DistributionSetTypeElement(this, (JpaSoftwareModuleType) smType, true)); return this; @@ -139,6 +166,10 @@ public class JpaDistributionSetType extends AbstractJpaNamedEntity implements Di @Override public DistributionSetType removeModuleType(final Long smTypeId) { + if (elements == null) { + return this; + } + // we search by id (standard equals compares also revison) final Optional found = elements.stream() .filter(element -> element.getSmType().getId().equals(smTypeId)).findFirst(); @@ -177,7 +208,11 @@ public class JpaDistributionSetType extends AbstractJpaNamedEntity implements Di } public Set getElements() { - return elements; + if (elements == null) { + return Collections.emptySet(); + } + + return Collections.unmodifiableSet(elements); } @Override diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaRollout.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaRollout.java index 6d720a75d..dc1b82ac2 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaRollout.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaRollout.java @@ -8,6 +8,7 @@ */ package org.eclipse.hawkbit.repository.jpa.model; +import java.util.Collections; import java.util.List; import javax.persistence.Column; @@ -108,11 +109,11 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event @Override public List getRolloutGroups() { - return rolloutGroups; - } + if (rolloutGroups == null) { + return Collections.emptyList(); + } - public void setRolloutGroups(final List rolloutGroups) { - this.rolloutGroups = rolloutGroups; + return Collections.unmodifiableList(rolloutGroups); } @Override diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaRolloutGroup.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaRolloutGroup.java index 750e61185..2c17a41c7 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaRolloutGroup.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaRolloutGroup.java @@ -8,7 +8,7 @@ */ package org.eclipse.hawkbit.repository.jpa.model; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; import javax.persistence.CascadeType; @@ -58,7 +58,7 @@ public class JpaRolloutGroup extends AbstractJpaNamedEntity implements RolloutGr @OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST }, targetEntity = RolloutTargetGroup.class) @JoinColumn(name = "rolloutGroup_Id", insertable = false, updatable = false) - private final List rolloutTargetGroup = new ArrayList<>(); + private List rolloutTargetGroup; @ManyToOne(fetch = FetchType.LAZY) private JpaRolloutGroup parent; @@ -92,7 +92,7 @@ public class JpaRolloutGroup extends AbstractJpaNamedEntity implements RolloutGr private String errorActionExp; @Column(name = "total_targets") - private long totalTargets; + private int totalTargets; @Transient private transient TotalTargetCountStatus totalTargetCountStatus; @@ -118,7 +118,11 @@ public class JpaRolloutGroup extends AbstractJpaNamedEntity implements RolloutGr } public List getRolloutTargetGroup() { - return rolloutTargetGroup; + if (rolloutTargetGroup == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(rolloutTargetGroup); } @Override @@ -201,11 +205,11 @@ public class JpaRolloutGroup extends AbstractJpaNamedEntity implements RolloutGr } @Override - public long getTotalTargets() { + public int getTotalTargets() { return totalTargets; } - public void setTotalTargets(final long totalTargets) { + public void setTotalTargets(final int totalTargets) { this.totalTargets = totalTargets; } @@ -223,7 +227,7 @@ public class JpaRolloutGroup extends AbstractJpaNamedEntity implements RolloutGr @Override public TotalTargetCountStatus getTotalTargetCountStatus() { if (totalTargetCountStatus == null) { - totalTargetCountStatus = new TotalTargetCountStatus(totalTargets); + totalTargetCountStatus = new TotalTargetCountStatus(new Long(totalTargets)); } return totalTargetCountStatus; } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java index e52857c37..49ff030d4 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java @@ -8,8 +8,8 @@ */ package org.eclipse.hawkbit.repository.jpa.model; -import java.util.ArrayList; import java.util.Collections; +import java.util.LinkedList; import java.util.List; import java.util.Optional; @@ -40,6 +40,8 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.persistence.annotations.CascadeOnDelete; +import com.google.common.collect.Lists; + /** * Base Software Module that is supported by OS level provisioning mechanism on * the edge controller, e.g. OS, JVM, AgentHub. @@ -64,7 +66,7 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement private JpaSoftwareModuleType type; @ManyToMany(mappedBy = "modules", targetEntity = JpaDistributionSet.class, fetch = FetchType.LAZY) - private final List assignedTo = new ArrayList<>(); + private List assignedTo; @Column(name = "deleted") private boolean deleted; @@ -82,13 +84,13 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement @CascadeOnDelete @OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE }, targetEntity = JpaSoftwareModuleMetadata.class) @JoinColumn(name = "sw_id", insertable = false, updatable = false) - private final List metadata = new ArrayList<>(); + private List metadata; /** * Default constructor. */ public JpaSoftwareModule() { - super(); + } /** @@ -119,7 +121,9 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement @Override public void addArtifact(final LocalArtifact artifact) { if (null == artifacts) { - artifacts = new ArrayList<>(4); + artifacts = new LinkedList<>(); + artifacts.add(artifact); + return; } if (!artifacts.contains(artifact)) { @@ -134,7 +138,9 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement @Override public void addArtifact(final ExternalArtifact artifact) { if (null == externalArtifacts) { - externalArtifacts = new ArrayList<>(4); + externalArtifacts = new LinkedList<>(); + externalArtifacts.add(artifact); + return; } if (!externalArtifacts.contains(artifact)) { @@ -143,28 +149,18 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement } - /** - * @param artifactId - * to look for - * @return found {@link Artifact} - */ @Override public Optional getLocalArtifact(final Long artifactId) { - if (null == artifacts) { + if (artifacts == null) { return Optional.empty(); } return artifacts.stream().filter(artifact -> artifact.getId().equals(artifactId)).findFirst(); } - /** - * @param fileName - * to look for - * @return found {@link Artifact} - */ @Override public Optional getLocalArtifactByFilename(final String fileName) { - if (null == artifacts) { + if (artifacts == null) { return Optional.empty(); } @@ -177,11 +173,18 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement */ @Override public List getArtifacts() { - final List result = new ArrayList<>(); - result.addAll(artifacts); + if (artifacts == null && externalArtifacts == null) { + return Collections.emptyList(); + } else if (artifacts == null) { + return Collections.unmodifiableList(externalArtifacts); + } else if (externalArtifacts == null) { + return Collections.unmodifiableList(artifacts); + } + + final List result = Lists.newLinkedList(artifacts); result.addAll(externalArtifacts); - return result; + return Collections.unmodifiableList(result); } /** @@ -207,7 +210,7 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement */ @Override public void removeArtifact(final LocalArtifact artifact) { - if (null != artifacts) { + if (artifacts != null) { artifacts.remove(artifact); } } @@ -218,7 +221,7 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement */ @Override public void removeArtifact(final ExternalArtifact artifact) { - if (null != externalArtifacts) { + if (externalArtifacts != null) { externalArtifacts.remove(artifact); } } @@ -248,11 +251,12 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement this.type = (JpaSoftwareModuleType) type; } - /** - * @return immutable list of meta data elements. - */ @Override public List getMetadata() { + if (metadata == null) { + return Collections.emptyList(); + } + return Collections.unmodifiableList(metadata); } @@ -262,12 +266,13 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement + ", revision=" + getOptLockRevision() + ", Id=" + getId() + ", type=" + getType().getName() + "]"; } - /** - * @return the assignedTo - */ @Override public List getAssignedTo() { - return assignedTo; + if (assignedTo == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(assignedTo); } } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTarget.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTarget.java index 476dea18b..7756c947e 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTarget.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTarget.java @@ -9,7 +9,9 @@ package org.eclipse.hawkbit.repository.jpa.model; import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Set; @@ -85,13 +87,13 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable tags = new HashSet<>(); + private Set tags; @CascadeOnDelete @OneToMany(fetch = FetchType.LAZY, orphanRemoval = true, cascade = { CascadeType.REMOVE }, targetEntity = JpaAction.class) @JoinColumn(name = "target", insertable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_act_hist_targ")) - private final List actions = new ArrayList<>(); + private List actions; @ManyToOne(optional = true, fetch = FetchType.LAZY, targetEntity = JpaDistributionSet.class) @JoinColumn(name = "assigned_distribution_set", nullable = true, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_assign_ds")) @@ -141,12 +143,8 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable getTags() { + if (tags == null) { + return Collections.emptySet(); + } + return tags; } + public boolean addTag(final TargetTag tag) { + if (tags == null) { + tags = new HashSet<>(); + } + + return tags.add(tag); + } + + public boolean removeTag(final TargetTag tag) { + if (tags == null) { + return false; + } + + return tags.remove(tag); + } + public void setAssignedDistributionSet(final DistributionSet assignedDistributionSet) { this.assignedDistributionSet = (JpaDistributionSet) assignedDistributionSet; } @@ -172,13 +190,21 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable tags) { - this.tags = tags; - } - @Override public List getActions() { - return actions; + if (actions == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(actions); + } + + public boolean addAction(final Action action) { + if (actions == null) { + actions = new LinkedList<>(); + } + + return actions.add(action); } @Override diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetInfo.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetInfo.java index ea3713fa3..6dbe2fcd7 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetInfo.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetInfo.java @@ -220,7 +220,7 @@ public class JpaTargetInfo implements Persistable, TargetInfo, EventAwareE return lastTargetQuery; } - public void setLastTargetQuery(final Long lastTargetQuery) { + public void setLastTargetQuery(final long lastTargetQuery) { this.lastTargetQuery = lastTargetQuery; } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetTag.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetTag.java index 96989f14a..a04933b6d 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetTag.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetTag.java @@ -8,6 +8,7 @@ */ package org.eclipse.hawkbit.repository.jpa.model; +import java.util.Collections; import java.util.List; import javax.persistence.Entity; @@ -65,7 +66,11 @@ public class JpaTargetTag extends AbstractJpaTag implements TargetTag { @Override public List getAssignedToTargets() { - return assignedToTargets; + if (assignedToTargets == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(assignedToTargets); } @Override diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/ThresholdRolloutGroupSuccessCondition.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/ThresholdRolloutGroupSuccessCondition.java index 3e098c701..676cd9ad1 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/ThresholdRolloutGroupSuccessCondition.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/ThresholdRolloutGroupSuccessCondition.java @@ -29,8 +29,8 @@ public class ThresholdRolloutGroupSuccessCondition implements RolloutGroupCondit @Override public boolean eval(final Rollout rollout, final RolloutGroup rolloutGroup, final String expression) { - final Long totalGroup = rolloutGroup.getTotalTargets(); - final Long finished = this.actionRepository.countByRolloutIdAndRolloutGroupIdAndStatus(rollout.getId(), + final long totalGroup = rolloutGroup.getTotalTargets(); + final long finished = this.actionRepository.countByRolloutIdAndRolloutGroupIdAndStatus(rollout.getId(), rolloutGroup.getId(), Action.Status.FINISHED); try { final Integer threshold = Integer.valueOf(expression); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java index 565513107..d5cc7f029 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java @@ -106,7 +106,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { assertThat(findActionsWithStatusCountByTarget).as("wrong action size").hasSize(1); assertThat(findActionsWithStatusCountByTarget.get(0).getActionStatusCount()).as("wrong action status size") - .isEqualTo(3L); + .isEqualTo(3); } @Test @@ -842,8 +842,8 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { final DistributionSet ds = testdataFactory.createDistributionSet("a"); // assign ds to create an action final DistributionSetAssignmentResult assignDistributionSet = deploymentManagement.assignDistributionSet( - ds.getId(), ActionType.SOFT, org.eclipse.hawkbit.repository.model.RepositoryModelConstants.NO_FORCE_TIME, - target.getControllerId()); + ds.getId(), ActionType.SOFT, + org.eclipse.hawkbit.repository.model.RepositoryModelConstants.NO_FORCE_TIME, target.getControllerId()); final Action action = deploymentManagement.findActionWithDetails(assignDistributionSet.getActions().get(0)); // verify preparation Action findAction = deploymentManagement.findAction(action.getId()); @@ -865,8 +865,8 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { final DistributionSet ds = testdataFactory.createDistributionSet("a"); // assign ds to create an action final DistributionSetAssignmentResult assignDistributionSet = deploymentManagement.assignDistributionSet( - ds.getId(), ActionType.FORCED, org.eclipse.hawkbit.repository.model.RepositoryModelConstants.NO_FORCE_TIME, - target.getControllerId()); + ds.getId(), ActionType.FORCED, + org.eclipse.hawkbit.repository.model.RepositoryModelConstants.NO_FORCE_TIME, target.getControllerId()); final Action action = deploymentManagement.findActionWithDetails(assignDistributionSet.getActions().get(0)); // verify perparation Action findAction = deploymentManagement.findAction(action.getId()); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DistributionSetManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DistributionSetManagementTest.java index b5ba12946..bcb6a1f32 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DistributionSetManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DistributionSetManagementTest.java @@ -396,14 +396,14 @@ public class DistributionSetManagementTest extends AbstractJpaIntegrationTest { // create a DS final DistributionSet ds = testdataFactory.createDistributionSet("testDs"); // initial opt lock revision must be zero - assertThat(ds.getOptLockRevision()).isEqualTo(1L); + assertThat(ds.getOptLockRevision()).isEqualTo(1); // create an DS meta data entry final DistributionSetMetadata dsMetadata = distributionSetManagement .createDistributionSetMetadata(new JpaDistributionSetMetadata(knownKey, ds, knownValue)); DistributionSet changedLockRevisionDS = distributionSetManagement.findDistributionSetById(ds.getId()); - assertThat(changedLockRevisionDS.getOptLockRevision()).isEqualTo(2L); + assertThat(changedLockRevisionDS.getOptLockRevision()).isEqualTo(2); // modifying the meta data value dsMetadata.setValue(knownUpdateValue); @@ -419,7 +419,7 @@ public class DistributionSetManagementTest extends AbstractJpaIntegrationTest { // module so opt lock // revision must be three changedLockRevisionDS = distributionSetManagement.findDistributionSetById(ds.getId()); - assertThat(changedLockRevisionDS.getOptLockRevision()).isEqualTo(3L); + assertThat(changedLockRevisionDS.getOptLockRevision()).isEqualTo(3); assertThat(changedLockRevisionDS.getLastModifiedAt()).isGreaterThan(0L); // verify updated meta data contains the updated value diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/SoftwareManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/SoftwareManagementTest.java index 739cf7f64..4f44d260c 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/SoftwareManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/SoftwareManagementTest.java @@ -848,7 +848,7 @@ public class SoftwareManagementTest extends AbstractJpaIntegrationTestWithMongoD final SoftwareModule ah = softwareManagement .createSoftwareModule(new JpaSoftwareModule(appType, "agent-hub", "1.0.1", null, "")); - assertThat(ah.getOptLockRevision()).isEqualTo(1L); + assertThat(ah.getOptLockRevision()).isEqualTo(1); final SoftwareModuleMetadata swMetadata1 = new JpaSoftwareModuleMetadata(knownKey1, ah, knownValue1); @@ -858,7 +858,7 @@ public class SoftwareManagementTest extends AbstractJpaIntegrationTestWithMongoD .createSoftwareModuleMetadata(Lists.newArrayList(swMetadata1, swMetadata2)); final SoftwareModule changedLockRevisionModule = softwareManagement.findSoftwareModuleById(ah.getId()); - assertThat(changedLockRevisionModule.getOptLockRevision()).isEqualTo(2L); + assertThat(changedLockRevisionModule.getOptLockRevision()).isEqualTo(2); assertThat(softwareModuleMetadata).hasSize(2); assertThat(softwareModuleMetadata.get(0)).isNotNull(); @@ -900,7 +900,7 @@ public class SoftwareManagementTest extends AbstractJpaIntegrationTestWithMongoD final SoftwareModule ah = softwareManagement .createSoftwareModule(new JpaSoftwareModule(appType, "agent-hub", "1.0.1", null, "")); // initial opt lock revision must be 1 - assertThat(ah.getOptLockRevision()).isEqualTo(1L); + assertThat(ah.getOptLockRevision()).isEqualTo(1); // create an software module meta data entry final List softwareModuleMetadata = softwareManagement.createSoftwareModuleMetadata( @@ -910,7 +910,7 @@ public class SoftwareManagementTest extends AbstractJpaIntegrationTestWithMongoD // because we are modifying the // base software module SoftwareModule changedLockRevisionModule = softwareManagement.findSoftwareModuleById(ah.getId()); - assertThat(changedLockRevisionModule.getOptLockRevision()).isEqualTo(2L); + assertThat(changedLockRevisionModule.getOptLockRevision()).isEqualTo(2); // modifying the meta data value softwareModuleMetadata.get(0).setValue(knownUpdateValue); @@ -925,7 +925,7 @@ public class SoftwareManagementTest extends AbstractJpaIntegrationTestWithMongoD // module so opt lock // revision must be two changedLockRevisionModule = softwareManagement.findSoftwareModuleById(ah.getId()); - assertThat(changedLockRevisionModule.getOptLockRevision()).isEqualTo(3L); + assertThat(changedLockRevisionModule.getOptLockRevision()).isEqualTo(3); // verify updated meta data contains the updated value assertThat(updated).isNotNull(); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementSearchTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementSearchTest.java index 1e8f102c7..35e326f4a 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementSearchTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementSearchTest.java @@ -686,7 +686,8 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest { final DistributionSet ds = testdataFactory.createDistributionSet("a"); - targAssigned = deploymentManagement.assignDistributionSet(ds, targAssigned).getAssignedEntity(); + targAssigned = Lists + .newLinkedList(deploymentManagement.assignDistributionSet(ds, targAssigned).getAssignedEntity()); targInstalled = deploymentManagement.assignDistributionSet(ds, targInstalled).getAssignedEntity(); targInstalled = sendUpdateActionStatusToTargets(ds, targInstalled, Status.FINISHED, "installed"); @@ -756,7 +757,7 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest { private List sendUpdateActionStatusToTargets(final DistributionSet dsA, final Iterable targs, final Status status, final String... msgs) { - final List result = new ArrayList(); + final List result = new ArrayList<>(); for (final Target t : targs) { final List findByTarget = actionRepository.findByTarget((JpaTarget) t); for (final Action action : findByTarget) { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementTest.java index 06f853db6..4671f701d 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementTest.java @@ -541,28 +541,30 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Tests the assigment of tags to the a single target.") public void targetTagAssignment() { - Target t1 = testdataFactory.generateTarget("id-1", "blablub"); + final Target t1 = testdataFactory.generateTarget("id-1", "blablub"); final int noT2Tags = 4; final int noT1Tags = 3; final List t1Tags = tagManagement .createTargetTags(testdataFactory.generateTargetTags(noT1Tags, "tag1")); - t1.getTags().addAll(t1Tags); - t1 = targetManagement.createTarget(t1); - Target t2 = testdataFactory.generateTarget("id-2", "blablub"); + t1Tags.forEach(tag -> ((JpaTarget) t1).addTag(tag)); + + targetManagement.createTarget(t1); + final Target t2 = testdataFactory.generateTarget("id-2", "blablub"); final List t2Tags = tagManagement .createTargetTags(testdataFactory.generateTargetTags(noT2Tags, "tag2")); - t2.getTags().addAll(t2Tags); - t2 = targetManagement.createTarget(t2); - t1 = targetManagement.findTargetByControllerID(t1.getControllerId()); - assertThat(t1.getTags()).as("Tag size is wrong").hasSize(noT1Tags).containsAll(t1Tags); - assertThat(t1.getTags()).as("Tag size is wrong").hasSize(noT1Tags) + t2Tags.forEach(tag -> ((JpaTarget) t2).addTag(tag)); + targetManagement.createTarget(t2); + + final Target t11 = targetManagement.findTargetByControllerID(t1.getControllerId()); + assertThat(t11.getTags()).as("Tag size is wrong").hasSize(noT1Tags).containsAll(t1Tags); + assertThat(t11.getTags()).as("Tag size is wrong").hasSize(noT1Tags) .doesNotContain(Iterables.toArray(t2Tags, TargetTag.class)); - t2 = targetManagement.findTargetByControllerID(t2.getControllerId()); - assertThat(t2.getTags()).as("Tag size is wrong").hasSize(noT2Tags).containsAll(t2Tags); - assertThat(t2.getTags()).as("Tag size is wrong").hasSize(noT2Tags) + final Target t21 = targetManagement.findTargetByControllerID(t2.getControllerId()); + assertThat(t21.getTags()).as("Tag size is wrong").hasSize(noT2Tags).containsAll(t2Tags); + assertThat(t21.getTags()).as("Tag size is wrong").hasSize(noT2Tags) .doesNotContain(Iterables.toArray(t1Tags, TargetTag.class)); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLActionFieldsTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLActionFieldsTest.java index 3138c9f86..884b02459 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLActionFieldsTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLActionFieldsTest.java @@ -18,7 +18,6 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaAction; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action.ActionType; -import org.eclipse.hawkbit.repository.model.Target; import org.junit.Before; import org.junit.Test; import org.springframework.data.domain.PageRequest; @@ -32,7 +31,7 @@ import ru.yandex.qatools.allure.annotations.Stories; @Stories("RSQL filter actions") public class RSQLActionFieldsTest extends AbstractJpaIntegrationTest { - private Target target; + private JpaTarget target; private JpaAction action; @Before @@ -42,7 +41,7 @@ public class RSQLActionFieldsTest extends AbstractJpaIntegrationTest { targetManagement.createTarget(target); action = new JpaAction(); action.setActionType(ActionType.SOFT); - target.getActions().add(action); + target.addAction(action); action.setTarget(target); actionRepository.save(action); for (int i = 0; i < 10; i++) { @@ -51,7 +50,7 @@ public class RSQLActionFieldsTest extends AbstractJpaIntegrationTest { newAction.setActive(i % 2 == 0); newAction.setTarget(target); actionRepository.save(newAction); - target.getActions().add(newAction); + target.addAction(newAction); } } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactDetailsLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactDetailsLayout.java index 8408f7c74..9a77af933 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactDetailsLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactDetailsLayout.java @@ -9,8 +9,8 @@ package org.eclipse.hawkbit.ui.artifacts.details; import java.util.ArrayList; +import java.util.Collections; import java.util.Date; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -46,6 +46,7 @@ import org.vaadin.spring.events.EventScope; import org.vaadin.spring.events.annotation.EventBusListenerMethod; import com.google.common.base.Strings; +import com.google.common.collect.Maps; import com.vaadin.data.Container; import com.vaadin.server.FontAwesome; import com.vaadin.shared.ui.label.ContentMode; @@ -207,8 +208,7 @@ public class ArtifactDetailsLayout extends VerticalLayout { } private Container createArtifactLazyQueryContainer() { - final Map queryConfiguration = new HashMap<>(); - return getArtifactLazyQueryContainer(queryConfiguration); + return getArtifactLazyQueryContainer(Collections.emptyMap()); } private LazyQueryContainer getArtifactLazyQueryContainer(final Map queryConfig) { @@ -429,9 +429,12 @@ public class ArtifactDetailsLayout extends VerticalLayout { titleOfArtifactDetails.setContentMode(ContentMode.HTML); } } - final Map queryConfiguration = new HashMap<>(); + final Map queryConfiguration; if (baseSwModuleId != null) { + queryConfiguration = Maps.newHashMapWithExpectedSize(1); queryConfiguration.put(SPUIDefinitions.BY_BASE_SOFTWARE_MODULE, baseSwModuleId); + } else { + queryConfiguration = Collections.emptyMap(); } final LazyQueryContainer artifactContainer = getArtifactLazyQueryContainer(queryConfiguration); artifactDetailsTable.setContainerDataSource(artifactContainer); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/event/UploadViewAcceptCriteria.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/event/UploadViewAcceptCriteria.java index 8cfeaea81..0fab13f84 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/event/UploadViewAcceptCriteria.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/event/UploadViewAcceptCriteria.java @@ -9,13 +9,13 @@ package org.eclipse.hawkbit.ui.artifacts.event; import java.util.Arrays; -import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.hawkbit.ui.common.AbstractAcceptCriteria; import org.eclipse.hawkbit.ui.utils.SPUIComponentIdProvider; +import com.google.gwt.thirdparty.guava.common.collect.Maps; import com.vaadin.spring.annotation.SpringComponent; import com.vaadin.spring.annotation.ViewScope; import com.vaadin.ui.Component; @@ -54,16 +54,17 @@ public class UploadViewAcceptCriteria extends AbstractAcceptCriteria { } private static Map> createDropConfigurations() { - final Map> config = new HashMap<>(); + final Map> config = Maps.newHashMapWithExpectedSize(1); // Delete drop area droppable components - config.put(SPUIComponentIdProvider.DELETE_BUTTON_WRAPPER_ID, Arrays.asList( - SPUIComponentIdProvider.UPLOAD_SOFTWARE_MODULE_TABLE, SPUIComponentIdProvider.UPLOAD_TYPE_BUTTON_PREFIX)); + config.put(SPUIComponentIdProvider.DELETE_BUTTON_WRAPPER_ID, + Arrays.asList(SPUIComponentIdProvider.UPLOAD_SOFTWARE_MODULE_TABLE, + SPUIComponentIdProvider.UPLOAD_TYPE_BUTTON_PREFIX)); return config; } private static Map createDropHintConfigurations() { - final Map config = new HashMap<>(); + final Map config = Maps.newHashMapWithExpectedSize(2); config.put(SPUIComponentIdProvider.UPLOAD_TYPE_BUTTON_PREFIX, UploadArtifactUIEvent.SOFTWARE_TYPE_DRAG_START); config.put(SPUIComponentIdProvider.UPLOAD_SOFTWARE_MODULE_TABLE, UploadArtifactUIEvent.SOFTWARE_DRAG_START); return config; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/footer/UploadViewConfirmationWindowLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/footer/UploadViewConfirmationWindowLayout.java index 49b271b6b..a8fee345c 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/footer/UploadViewConfirmationWindowLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/footer/UploadViewConfirmationWindowLayout.java @@ -9,7 +9,6 @@ package org.eclipse.hawkbit.ui.artifacts.footer; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -26,6 +25,7 @@ import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions; import org.springframework.beans.factory.annotation.Autowired; +import com.google.gwt.thirdparty.guava.common.collect.Maps; import com.vaadin.data.Container; import com.vaadin.data.Item; import com.vaadin.data.util.IndexedContainer; @@ -64,7 +64,7 @@ public class UploadViewConfirmationWindowLayout extends AbstractConfirmationWind @Override protected Map getConfimrationTabs() { - final Map tabs = new HashMap<>(); + final Map tabs = Maps.newHashMapWithExpectedSize(2); if (!artifactUploadState.getDeleteSofwareModules().isEmpty()) { tabs.put(i18n.get("caption.delete.swmodule.accordion.tab"), createSMDeleteConfirmationTab()); } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleTable.java index 98e309396..7fd895623 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleTable.java @@ -8,7 +8,6 @@ */ package org.eclipse.hawkbit.ui.artifacts.smtable; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -37,6 +36,7 @@ import org.vaadin.addons.lazyquerycontainer.LazyQueryDefinition; import org.vaadin.spring.events.EventScope; import org.vaadin.spring.events.annotation.EventBusListenerMethod; +import com.google.gwt.thirdparty.guava.common.collect.Maps; import com.vaadin.data.Container; import com.vaadin.data.Item; import com.vaadin.event.dd.DragAndDropEvent; @@ -97,7 +97,7 @@ public class SoftwareModuleTable extends AbstractNamedVersionTable prepareQueryConfigFilters() { - final Map queryConfig = new HashMap<>(); + final Map queryConfig = Maps.newHashMapWithExpectedSize(2); artifactUploadState.getSoftwareModuleFilters().getSearchText() .ifPresent(value -> queryConfig.put(SPUIDefinitions.FILTER_BY_TEXT, value)); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/customrenderers/client/renderers/HtmlLabelRenderer.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/customrenderers/client/renderers/HtmlLabelRenderer.java index c6b2d9b64..535150f04 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/customrenderers/client/renderers/HtmlLabelRenderer.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/customrenderers/client/renderers/HtmlLabelRenderer.java @@ -8,9 +8,9 @@ */ package org.eclipse.hawkbit.ui.customrenderers.client.renderers; -import java.util.HashMap; import java.util.Map; +import com.google.common.collect.Maps; import com.google.gwt.core.client.GWT; import com.vaadin.client.renderers.WidgetRenderer; import com.vaadin.client.ui.VLabel; @@ -29,11 +29,11 @@ public class HtmlLabelRenderer extends WidgetRenderer { } @Override - public void render(RendererCellReference cell, String input, VLabel label) { - Map map = formatInput(input); - String value = map.containsKey("value") ? map.get("value") : null; - String style = map.containsKey("style") ? map.get("style") : null; - String id = map.containsKey("id") ? map.get("id") : null; + public void render(final RendererCellReference cell, final String input, final VLabel label) { + final Map map = formatInput(input); + final String value = map.containsKey("value") ? map.get("value") : null; + final String style = map.containsKey("style") ? map.get("style") : null; + final String id = map.containsKey("id") ? map.get("id") : null; if (value != null) { label.setHTML("&#x" + Integer.toHexString(Integer.parseInt(value)) + ";"); @@ -44,7 +44,7 @@ public class HtmlLabelRenderer extends WidgetRenderer { label.getElement().setId(id); } - private void applyStyle(VLabel label, String style) { + private void applyStyle(final VLabel label, final String style) { label.setStyleName(VLabel.CLASSNAME); label.addStyleName(getStyle("small")); label.addStyleName(getStyle("font-icon")); @@ -57,11 +57,12 @@ public class HtmlLabelRenderer extends WidgetRenderer { return new StringBuilder(style).append(" ").append(VLabel.CLASSNAME).append("-").append(style).toString(); } - private Map formatInput(String input) { - Map details = new HashMap<>(); - String[] tempData = input.split(","); - for (String statusWithCount : tempData) { - String[] statusWithCountList = statusWithCount.split(":"); + private Map formatInput(final String input) { + + final String[] tempData = input.split(","); + final Map details = Maps.newHashMapWithExpectedSize(tempData.length); + for (final String statusWithCount : tempData) { + final String[] statusWithCountList = statusWithCount.split(":"); details.put(statusWithCountList[0], statusWithCountList[1]); } return details; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetDetails.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetDetails.java index 06b23c51b..8d474bede 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetDetails.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetDetails.java @@ -93,7 +93,7 @@ public class DistributionSetDetails extends AbstractNamedVersionedEntityTableDet private VerticalLayout tagsLayout; - private final Map assignedSWModule = new HashMap<>(); + private Map assignedSWModule; /** * softwareLayout Initialize the component. @@ -144,6 +144,10 @@ public class DistributionSetDetails extends AbstractNamedVersionedEntityTableDet } if (null != softwareModuleIdNameList) { + if (assignedSWModule == null) { + assignedSWModule = new HashMap<>(); + } + for (final SoftwareModuleIdName swIdName : softwareModuleIdNameList) { final SoftwareModule softwareModule = softwareManagement.findSoftwareModuleById(swIdName.getId()); if (assignedSWModule.containsKey(softwareModule.getType().getName())) { @@ -186,6 +190,9 @@ public class DistributionSetDetails extends AbstractNamedVersionedEntityTableDet @SuppressWarnings("unchecked") private void updateSoftwareModule(final SoftwareModule module) { + if (assignedSWModule == null) { + assignedSWModule = new HashMap<>(); + } softwareModuleTable.getContainerDataSource().getItemIds(); if (assignedSWModule.containsKey(module.getType().getName())) { @@ -363,7 +370,9 @@ public class DistributionSetDetails extends AbstractNamedVersionedEntityTableDet if ((saveActionWindowEvent == SaveActionWindowEvent.SAVED_ASSIGNMENTS || saveActionWindowEvent == SaveActionWindowEvent.DISCARD_ALL_ASSIGNMENTS) && getSelectedBaseEntity() != null) { - assignedSWModule.clear(); + if (assignedSWModule != null) { + assignedSWModule.clear(); + } setSelectedBaseEntity( distributionSetManagement.findDistributionSetByIdWithDetails(getSelectedBaseEntityId())); UI.getCurrent().access(() -> populateModule()); @@ -375,7 +384,9 @@ public class DistributionSetDetails extends AbstractNamedVersionedEntityTableDet if (saveActionWindowEvent == SaveActionWindowEvent.DISCARD_ASSIGNMENT || saveActionWindowEvent == SaveActionWindowEvent.DISCARD_ALL_ASSIGNMENTS || saveActionWindowEvent == SaveActionWindowEvent.DELETE_ALL_SOFWARE) { - assignedSWModule.clear(); + if (assignedSWModule != null) { + assignedSWModule.clear(); + } showUnsavedAssignment(); } } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetTable.java index 94bc6690e..fc21ddd5b 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetTable.java @@ -57,6 +57,7 @@ import org.vaadin.addons.lazyquerycontainer.LazyQueryDefinition; import org.vaadin.spring.events.EventScope; import org.vaadin.spring.events.annotation.EventBusListenerMethod; +import com.google.common.collect.Maps; import com.vaadin.data.Container; import com.vaadin.data.Item; import com.vaadin.event.dd.DragAndDropEvent; @@ -167,7 +168,7 @@ public class DistributionSetTable extends AbstractNamedVersionTable prepareQueryConfigFilters() { - final Map queryConfig = new HashMap<>(); + final Map queryConfig = Maps.newHashMapWithExpectedSize(2); manageDistUIState.getManageDistFilters().getSearchText() .ifPresent(value -> queryConfig.put(SPUIDefinitions.FILTER_BY_TEXT, value)); @@ -530,7 +531,7 @@ public class DistributionSetTable extends AbstractNamedVersionTable> createDropConfigurations() { - final Map> config = new HashMap<>(); + final Map> config = Maps.newHashMapWithExpectedSize(2); // Delete drop area droppable components config.put(SPUIComponentIdProvider.DELETE_BUTTON_WRAPPER_ID, @@ -82,7 +82,7 @@ public class DistributionsViewAcceptCriteria extends AbstractAcceptCriteria { } private static Map createDropHintConfigurations() { - final Map config = new HashMap<>(); + final Map config = Maps.newHashMapWithExpectedSize(4); config.put(SPUIDefinitions.DISTRIBUTION_TYPE_ID_PREFIXS, DragEvent.DISTRIBUTION_TYPE_DRAG); config.put(SPUIComponentIdProvider.DIST_TABLE_ID, DragEvent.DISTRIBUTION_DRAG); config.put(SPUIComponentIdProvider.UPLOAD_SOFTWARE_MODULE_TABLE, DragEvent.SOFTWAREMODULE_DRAG); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/footer/DistributionsConfirmationWindowLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/footer/DistributionsConfirmationWindowLayout.java index 290d2ff54..bc8c3a28b 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/footer/DistributionsConfirmationWindowLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/footer/DistributionsConfirmationWindowLayout.java @@ -8,7 +8,6 @@ */ package org.eclipse.hawkbit.ui.distributions.footer; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -37,6 +36,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.vaadin.spring.events.EventScope; import org.vaadin.spring.events.annotation.EventBusListenerMethod; +import com.google.common.collect.Maps; import com.vaadin.data.Container; import com.vaadin.data.Item; import com.vaadin.data.util.IndexedContainer; @@ -90,7 +90,7 @@ public class DistributionsConfirmationWindowLayout extends AbstractConfirmationW @Override protected Map getConfimrationTabs() { - final Map tabs = new HashMap<>(); + final Map tabs = Maps.newHashMapWithExpectedSize(5); /* Create tab for SW Modules delete */ if (!manageDistUIState.getDeleteSofwareModulesList().isEmpty()) { tabs.put(i18n.get("caption.delete.swmodule.accordion.tab"), createSMDeleteConfirmationTab()); @@ -112,7 +112,6 @@ public class DistributionsConfirmationWindowLayout extends AbstractConfirmationW } /* Create tab for Assign Software Module */ - if (!manageDistUIState.getAssignedList().isEmpty()) { tabs.put(i18n.get("caption.assign.dist.accordion.tab"), createAssignSWModuleConfirmationTab()); } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/smtable/SwModuleTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/smtable/SwModuleTable.java index 9af813788..150deaaf7 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/smtable/SwModuleTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/smtable/SwModuleTable.java @@ -8,7 +8,6 @@ */ package org.eclipse.hawkbit.ui.distributions.smtable; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -40,6 +39,7 @@ import org.vaadin.addons.lazyquerycontainer.LazyQueryDefinition; import org.vaadin.spring.events.EventScope; import org.vaadin.spring.events.annotation.EventBusListenerMethod; +import com.google.common.collect.Maps; import com.vaadin.data.Container; import com.vaadin.data.Item; import com.vaadin.event.dd.DragAndDropEvent; @@ -143,7 +143,7 @@ public class SwModuleTable extends AbstractNamedVersionTable prepareQueryConfigFilters() { - final Map queryConfig = new HashMap<>(); + final Map queryConfig = Maps.newHashMapWithExpectedSize(3); manageDistUIState.getSoftwareModuleFilters().getSearchText() .ifPresent(value -> queryConfig.put(SPUIDefinitions.FILTER_BY_TEXT, value)); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/smtype/DistSMTypeFilterButtons.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/smtype/DistSMTypeFilterButtons.java index 9fe3ec3f1..c061b1389 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/smtype/DistSMTypeFilterButtons.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/smtype/DistSMTypeFilterButtons.java @@ -13,8 +13,7 @@ import static org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleTypeEvent.Sof import static org.eclipse.hawkbit.ui.utils.SPUIComponentIdProvider.SW_MODULE_TYPE_TABLE_ID; import static org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions.VAR_NAME; -import java.util.HashMap; -import java.util.Map; +import java.util.Collections; import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleTypeEvent; import org.eclipse.hawkbit.ui.common.SoftwareModuleTypeBeanQuery; @@ -59,10 +58,9 @@ public class DistSMTypeFilterButtons extends AbstractFilterButtons { @Override protected LazyQueryContainer createButtonsLazyQueryContainer() { - final Map queryConfig = new HashMap<>(); final BeanQueryFactory typeQF = new BeanQueryFactory<>( SoftwareModuleTypeBeanQuery.class); - typeQF.setQueryConfiguration(queryConfig); + typeQF.setQueryConfiguration(Collections.emptyMap()); return new LazyQueryContainer(new LazyQueryDefinition(true, 20, VAR_NAME), typeQF); } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/state/ManageDistUIState.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/state/ManageDistUIState.java index caac53147..3a2075afc 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/state/ManageDistUIState.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/state/ManageDistUIState.java @@ -52,7 +52,7 @@ public class ManageDistUIState implements ManagmentEntityState selectedSoftwareModules = emptySet(); - private Set selectedDeleteDistSetTypes = new HashSet<>(); + private final Set selectedDeleteDistSetTypes = new HashSet<>(); private Set selectedDeleteSWModuleTypes = new HashSet<>(); @@ -201,10 +201,6 @@ public class ManageDistUIState implements ManagmentEntityState selectedDeleteDistSetTypes) { - this.selectedDeleteDistSetTypes = selectedDeleteDistSetTypes; - } - public Set getSelectedDeleteSWModuleTypes() { return selectedDeleteSWModuleTypes; } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterTable.java index e447a257a..89df1c615 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterTable.java @@ -10,7 +10,6 @@ package org.eclipse.hawkbit.ui.filtermanagement; import java.util.ArrayList; import java.util.Date; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -37,6 +36,7 @@ import org.vaadin.spring.events.EventScope; import org.vaadin.spring.events.annotation.EventBusListenerMethod; import com.google.common.base.Strings; +import com.google.common.collect.Maps; import com.vaadin.data.Item; import com.vaadin.server.FontAwesome; import com.vaadin.shared.ui.label.ContentMode; @@ -142,7 +142,7 @@ public class CreateOrUpdateFilterTable extends Table { } private Map prepareQueryConfigFilters() { - final Map queryConfig = new HashMap<>(); + final Map queryConfig = Maps.newHashMapWithExpectedSize(2); if (!Strings.isNullOrEmpty(filterManagementUIState.getFilterQueryValue())) { queryConfig.put(SPUIDefinitions.FILTER_BY_QUERY, filterManagementUIState.getFilterQueryValue()); } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterTable.java index 5ee3be084..1ca72d855 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterTable.java @@ -38,6 +38,7 @@ import org.vaadin.spring.events.EventBus; import org.vaadin.spring.events.EventScope; import org.vaadin.spring.events.annotation.EventBusListenerMethod; +import com.google.common.collect.Maps; import com.vaadin.data.Container; import com.vaadin.data.Item; import com.vaadin.server.FontAwesome; @@ -128,7 +129,7 @@ public class TargetFilterTable extends Table { } private Map prepareQueryConfigFilters() { - final Map queryConfig = new HashMap<>(); + final Map queryConfig = Maps.newHashMapWithExpectedSize(1); filterManagementUIState.getCustomFilterSearchText() .ifPresent(value -> queryConfig.put(SPUIDefinitions.FILTER_BY_TEXT, value)); return queryConfig; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionAddUpdateWindowLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionAddUpdateWindowLayout.java index e0bf9b1df..eb66f02a6 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionAddUpdateWindowLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionAddUpdateWindowLayout.java @@ -8,9 +8,8 @@ */ package org.eclipse.hawkbit.ui.management.dstable; -import java.util.HashMap; +import java.util.Collections; import java.util.HashSet; -import java.util.Map; import java.util.Set; import javax.annotation.PostConstruct; @@ -178,10 +177,9 @@ public class DistributionAddUpdateWindowLayout extends CustomComponent { * @return */ private LazyQueryContainer getDistSetTypeLazyQueryContainer() { - final Map queryConfig = new HashMap<>(); final BeanQueryFactory dtQF = new BeanQueryFactory<>( DistributionSetTypeBeanQuery.class); - dtQF.setQueryConfiguration(queryConfig); + dtQF.setQueryConfiguration(Collections.emptyMap()); final LazyQueryContainer disttypeContainer = new LazyQueryContainer( new LazyQueryDefinition(true, SPUIDefinitions.DIST_TYPE_SIZE, SPUILabelDefinitions.VAR_NAME), dtQF); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTable.java index 3bf60df52..aae6ba477 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTable.java @@ -9,7 +9,6 @@ package org.eclipse.hawkbit.ui.management.dstable; import java.util.ArrayList; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -56,6 +55,7 @@ import org.vaadin.addons.lazyquerycontainer.LazyQueryDefinition; import org.vaadin.spring.events.EventScope; import org.vaadin.spring.events.annotation.EventBusListenerMethod; +import com.google.common.collect.Maps; import com.vaadin.data.Container; import com.vaadin.data.Item; import com.vaadin.event.dd.DragAndDropEvent; @@ -240,7 +240,7 @@ public class DistributionTable extends AbstractNamedVersionTable prepareQueryConfigFilters() { - final Map queryConfig = new HashMap<>(); + final Map queryConfig = Maps.newHashMapWithExpectedSize(4); managementUIState.getDistributionTableFilters().getSearchText() .ifPresent(value -> queryConfig.put(SPUIDefinitions.FILTER_BY_TEXT, value)); managementUIState.getDistributionTableFilters().getPinnedTargetId() diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstag/DistributionTagButtons.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstag/DistributionTagButtons.java index 8b5ee85cf..d4da4d764 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstag/DistributionTagButtons.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstag/DistributionTagButtons.java @@ -8,8 +8,7 @@ */ package org.eclipse.hawkbit.ui.management.dstag; -import java.util.HashMap; -import java.util.Map; +import java.util.Collections; import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.eventbus.event.DistributionSetTagCreatedBulkEvent; @@ -101,9 +100,8 @@ public class DistributionTagButtons extends AbstractFilterButtons { @Override protected LazyQueryContainer createButtonsLazyQueryContainer() { - final Map queryConfig = new HashMap<>(); final BeanQueryFactory tagQF = new BeanQueryFactory<>(DistributionTagBeanQuery.class); - tagQF.setQueryConfiguration(queryConfig); + tagQF.setQueryConfiguration(Collections.emptyMap()); return HawkbitCommonUtil.createDSLazyQueryContainer( new BeanQueryFactory(DistributionTagBeanQuery.class)); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/event/ManagementViewAcceptCriteria.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/event/ManagementViewAcceptCriteria.java index be50f7b79..629fea06e 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/event/ManagementViewAcceptCriteria.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/event/ManagementViewAcceptCriteria.java @@ -9,7 +9,6 @@ package org.eclipse.hawkbit.ui.management.event; import java.util.Arrays; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -17,6 +16,7 @@ import org.eclipse.hawkbit.ui.common.AbstractAcceptCriteria; import org.eclipse.hawkbit.ui.utils.SPUIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; +import com.google.common.collect.Maps; import com.vaadin.spring.annotation.SpringComponent; import com.vaadin.spring.annotation.ViewScope; import com.vaadin.ui.Component; @@ -67,7 +67,7 @@ public class ManagementViewAcceptCriteria extends AbstractAcceptCriteria { } private static Map> createDropConfigurations() { - final Map> config = new HashMap<>(); + final Map> config = Maps.newHashMapWithExpectedSize(6); // Delete drop area acceptable components config.put(SPUIComponentIdProvider.DELETE_BUTTON_WRAPPER_ID, @@ -82,7 +82,8 @@ public class ManagementViewAcceptCriteria extends AbstractAcceptCriteria { Arrays.asList(SPUIDefinitions.TARGET_TAG_ID_PREFIXS, SPUIComponentIdProvider.DIST_TABLE_ID)); // Target table header acceptable components - config.put(SPUIComponentIdProvider.TARGET_DROP_FILTER_ICON, Arrays.asList(SPUIComponentIdProvider.DIST_TABLE_ID)); + config.put(SPUIComponentIdProvider.TARGET_DROP_FILTER_ICON, + Arrays.asList(SPUIComponentIdProvider.DIST_TABLE_ID)); // Distribution table acceptable components config.put(SPUIComponentIdProvider.DIST_TABLE_ID, Arrays.asList(SPUIDefinitions.TARGET_TAG_ID_PREFIXS, @@ -94,7 +95,7 @@ public class ManagementViewAcceptCriteria extends AbstractAcceptCriteria { } private static Map createDropHintConfigurations() { - final Map config = new HashMap<>(); + final Map config = Maps.newHashMapWithExpectedSize(4); config.put(SPUIDefinitions.TARGET_TAG_ID_PREFIXS, DragEvent.TARGET_TAG_DRAG); config.put(SPUIComponentIdProvider.TARGET_TABLE_ID, DragEvent.TARGET_DRAG); config.put(SPUIComponentIdProvider.DIST_TABLE_ID, DragEvent.DISTRIBUTION_DRAG); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/footer/ManangementConfirmationWindowLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/footer/ManangementConfirmationWindowLayout.java index e4c56b165..4524f6d9c 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/footer/ManangementConfirmationWindowLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/footer/ManangementConfirmationWindowLayout.java @@ -39,6 +39,7 @@ import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions; import org.springframework.beans.factory.annotation.Autowired; +import com.google.common.collect.Maps; import com.vaadin.data.Item; import com.vaadin.data.util.IndexedContainer; import com.vaadin.server.FontAwesome; @@ -88,7 +89,7 @@ public class ManangementConfirmationWindowLayout extends AbstractConfirmationWin @Override protected Map getConfimrationTabs() { - final Map tabs = new HashMap<>(); + final Map tabs = Maps.newHashMapWithExpectedSize(3); if (!managementUIState.getDeletedDistributionList().isEmpty()) { tabs.put(i18n.get("caption.delete.dist.accordion.tab"), createDeletedDistributionTab()); } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/state/ManagementUIState.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/state/ManagementUIState.java index ac3261895..2f40f5def 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/state/ManagementUIState.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/state/ManagementUIState.java @@ -87,10 +87,9 @@ public class ManagementUIState implements ManagmentEntityState queryConfiguration = new HashMap<>(); + final Map queryConfiguration = Maps.newHashMapWithExpectedSize(2); final List list = new ArrayList<>(); queryConfiguration.put(SPUIDefinitions.FILTER_BY_NO_TAG, diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetTable.java index 26211f67d..7f53b729e 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetTable.java @@ -19,10 +19,10 @@ import static org.eclipse.hawkbit.ui.management.event.TargetFilterEvent.REMOVE_F import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -43,12 +43,6 @@ import org.eclipse.hawkbit.ui.common.ManagmentEntityState; import org.eclipse.hawkbit.ui.common.UserDetailsFormatter; import org.eclipse.hawkbit.ui.common.table.AbstractTable; import org.eclipse.hawkbit.ui.common.table.BaseEntityEventType; -import org.eclipse.hawkbit.ui.filter.FilterExpression; -import org.eclipse.hawkbit.ui.filter.Filters; -import org.eclipse.hawkbit.ui.filter.target.CustomTargetFilter; -import org.eclipse.hawkbit.ui.filter.target.TargetSearchTextFilter; -import org.eclipse.hawkbit.ui.filter.target.TargetStatusFilter; -import org.eclipse.hawkbit.ui.filter.target.TargetTagFilter; import org.eclipse.hawkbit.ui.management.event.DragEvent; import org.eclipse.hawkbit.ui.management.event.ManagementUIEvent; import org.eclipse.hawkbit.ui.management.event.ManagementViewAcceptCriteria; @@ -78,6 +72,7 @@ import org.vaadin.spring.events.EventScope; import org.vaadin.spring.events.annotation.EventBusListenerMethod; import com.google.common.base.Strings; +import com.google.common.collect.Maps; import com.vaadin.data.Container; import com.vaadin.data.Item; import com.vaadin.event.dd.DragAndDropEvent; @@ -140,11 +135,14 @@ public class TargetTable extends AbstractTable { if (TargetCreatedEvent.class.isInstance(firstEvent)) { onTargetCreatedEvents(); } else if (TargetInfoUpdateEvent.class.isInstance(firstEvent)) { - onTargetInfoUpdateEvents((List) events); + onTargetUpdateEvents(((List) events).stream() + .map(targetInfoUpdateEvent -> targetInfoUpdateEvent.getEntity().getTarget()) + .collect(Collectors.toList())); } else if (TargetDeletedEvent.class.isInstance(firstEvent)) { onTargetDeletedEvent((List) events); } else if (TargetUpdatedEvent.class.isInstance(firstEvent)) { - onTargetUpdateEvents((List) events); + onTargetUpdateEvents(((List) events).stream() + .map(targetInfoUpdateEvent -> targetInfoUpdateEvent.getEntity()).collect(Collectors.toList())); } } @@ -348,7 +346,7 @@ public class TargetTable extends AbstractTable { } private Map prepareQueryConfigFilters() { - final Map queryConfig = new HashMap<>(); + final Map queryConfig = Maps.newHashMapWithExpectedSize(7); managementUIState.getTargetTableFilters().getSearchText() .ifPresent(value -> queryConfig.put(SPUIDefinitions.FILTER_BY_TEXT, value)); managementUIState.getTargetTableFilters().getDistributionSet() @@ -746,16 +744,17 @@ public class TargetTable extends AbstractTable { } @SuppressWarnings("unchecked") - private void updateVisibleItemOnEvent(final TargetInfo targetInfo, final Target target, - final TargetIdName targetIdName) { + private void updateVisibleItemOnEvent(final TargetInfo targetInfo) { + final Target target = targetInfo.getTarget(); + final TargetIdName targetIdName = target.getTargetIdName(); + final LazyQueryContainer targetContainer = (LazyQueryContainer) getContainerDataSource(); final Item item = targetContainer.getItem(targetIdName); + item.getItemProperty(SPUILabelDefinitions.VAR_NAME).setValue(target.getName()); - if (targetInfo != null) { - item.getItemProperty(SPUILabelDefinitions.VAR_POLL_STATUS_TOOL_TIP) - .setValue(HawkbitCommonUtil.getPollStatusToolTip(targetInfo.getPollStatus(), i18n)); - item.getItemProperty(SPUILabelDefinitions.VAR_TARGET_STATUS).setValue(targetInfo.getUpdateStatus()); - } + item.getItemProperty(SPUILabelDefinitions.VAR_POLL_STATUS_TOOL_TIP) + .setValue(HawkbitCommonUtil.getPollStatusToolTip(targetInfo.getPollStatus(), i18n)); + item.getItemProperty(SPUILabelDefinitions.VAR_TARGET_STATUS).setValue(targetInfo.getUpdateStatus()); } private boolean isLastSelectedTarget(final TargetIdName targetIdName) { @@ -767,62 +766,30 @@ public class TargetTable extends AbstractTable { * EventListener method which is called by the event bus to notify about a * list of {@link TargetInfoUpdateEvent}. * - * @param targetInfoUpdateEvents - * list of target info update event + * @param updatedTargets + * list of updated targets */ - private void onTargetInfoUpdateEvents(final List targetInfoUpdateEvents) { + private void onTargetUpdateEvents(final List updatedTargets) { @SuppressWarnings("unchecked") final List visibleItemIds = (List) getVisibleItemIds(); - boolean shoulTargetsUpdated = false; - Target lastSelectedTarget = null; - for (final TargetInfoUpdateEvent targetInfoUpdateEvent : targetInfoUpdateEvents) { - final TargetInfo targetInfo = targetInfoUpdateEvent.getEntity(); - final Target target = targetInfo.getTarget(); - final TargetIdName targetIdName = target.getTargetIdName(); - if (Filters.or(getTargetTableFilters(target)).doFilter()) { - shoulTargetsUpdated = true; - } else { - if (visibleItemIds.contains(targetIdName)) { - updateVisibleItemOnEvent(targetInfo, target, targetIdName); - } - } - // workaround until push is available for action history, re-select - // the updated target so the action history gets refreshed. - if (isLastSelectedTarget(targetIdName)) { - lastSelectedTarget = target; - } - } - if (shoulTargetsUpdated) { - refreshTargets(); - } - if (lastSelectedTarget != null) { - eventBus.publish(this, new TargetTableEvent(BaseEntityEventType.SELECTED_ENTITY, lastSelectedTarget)); - } - } - private void onTargetUpdateEvents(final List events) { - final List visibleItemIds = (List) getVisibleItemIds(); - boolean shoulTargetsUpdated = false; - Target lastSelectedTarget = null; - for (final TargetUpdatedEvent targetUpdatedEvent : events) { - final Target target = targetUpdatedEvent.getEntity(); - final TargetIdName targetIdName = target.getTargetIdName(); - if (Filters.or(getTargetTableFilters(target)).doFilter()) { - shoulTargetsUpdated = true; - } else { - if (visibleItemIds.contains(targetIdName)) { - updateVisibleItemOnEvent(null, target, targetIdName); - } - } - if (isLastSelectedTarget(targetIdName)) { - lastSelectedTarget = target; - } - } - if (shoulTargetsUpdated) { + if (isFilterEnabled()) { + LOG.debug("Filter enabled on UI {}. Refresh targets from database.", getUI().getUIId()); refreshTargets(); + } else { + updatedTargets.stream().filter(target -> visibleItemIds.contains(target.getTargetIdName())) + .forEach(target -> updateVisibleItemOnEvent(target.getTargetInfo())); } - if (lastSelectedTarget != null) { - eventBus.publish(this, new TargetTableEvent(BaseEntityEventType.SELECTED_ENTITY, lastSelectedTarget)); + + // workaround until push is available for action + // history, re-select + // the updated target so the action history gets + // refreshed. + final Optional selected = updatedTargets.stream() + .filter(target -> isLastSelectedTarget(target.getTargetIdName())).findAny(); + if (selected.isPresent()) { + LOG.debug("Selected element has changed on UI {}. Reselect to update action history.", getUI().getUIId()); + eventBus.publish(this, new TargetTableEvent(BaseEntityEventType.SELECTED_ENTITY, selected.get())); } } @@ -830,17 +797,11 @@ public class TargetTable extends AbstractTable { refreshTargets(); } - private List getTargetTableFilters(final Target target) { + private boolean isFilterEnabled() { final TargetTableFilters targetTableFilters = managementUIState.getTargetTableFilters(); - final List filters = new ArrayList<>(); - if (targetTableFilters.getSearchText().isPresent()) { - filters.add(new TargetSearchTextFilter(target, targetTableFilters.getSearchText().get())); - } - filters.add(new TargetStatusFilter(targetTableFilters.getClickedStatusTargetTags())); - filters.add(new TargetTagFilter(target, targetTableFilters.getClickedTargetTags(), - targetTableFilters.isNoTagSelected())); - filters.add(new CustomTargetFilter(targetTableFilters.getTargetFilterQuery())); - return filters; + return targetTableFilters.getSearchText().isPresent() || !targetTableFilters.getClickedTargetTags().isEmpty() + || !targetTableFilters.getClickedStatusTargetTags().isEmpty() + || targetTableFilters.getTargetFilterQuery().isPresent(); } /** diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/TargetFilterQueryButtons.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/TargetFilterQueryButtons.java index 571656c64..2a45af064 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/TargetFilterQueryButtons.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/TargetFilterQueryButtons.java @@ -9,9 +9,8 @@ package org.eclipse.hawkbit.ui.management.targettag; import java.util.ArrayList; -import java.util.HashMap; +import java.util.Collections; import java.util.List; -import java.util.Map; import javax.annotation.PreDestroy; @@ -102,8 +101,7 @@ public class TargetFilterQueryButtons extends Table { protected LazyQueryContainer createButtonsLazyQueryContainer() { final BeanQueryFactory queryFactory = new BeanQueryFactory<>( TargetFilterBeanQuery.class); - final Map queryConfig = new HashMap<>(); - queryFactory.setQueryConfiguration(queryConfig); + queryFactory.setQueryConfiguration(Collections.emptyMap()); return new LazyQueryContainer(new LazyQueryDefinition(true, 20, "id"), queryFactory); } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/push/DelayedEventBusPushStrategy.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/push/DelayedEventBusPushStrategy.java index 023979ead..bbb9335a3 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/push/DelayedEventBusPushStrategy.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/push/DelayedEventBusPushStrategy.java @@ -8,11 +8,11 @@ */ package org.eclipse.hawkbit.ui.push; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.concurrent.BlockingDeque; -import java.util.concurrent.Executors; +import java.util.concurrent.ExecutionException; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; @@ -57,10 +57,11 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { private static final Logger LOG = LoggerFactory.getLogger(DelayedEventBusPushStrategy.class); private static final int BLOCK_SIZE = 10_000; - private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); + private final ScheduledExecutorService executorService; private final BlockingDeque queue = new LinkedBlockingDeque<>(BLOCK_SIZE); private final EventBus.SessionEventBus eventBus; private final com.google.common.eventbus.EventBus systemEventBus; + private int uiid = -1; private ScheduledFuture jobHandle; @@ -68,15 +69,20 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { /** * Constructor. - * + * + * @param executorService + * for scheduled execution of event forwarding to the UI * @param eventBus * the session event bus to where the events should be dispatched * @param systemEventBus * the system event bus where to retrieve the events from the * back-end + * @param eventProvider + * for event delegation to UI */ - public DelayedEventBusPushStrategy(final SessionEventBus eventBus, + public DelayedEventBusPushStrategy(final ScheduledExecutorService executorService, final SessionEventBus eventBus, final com.google.common.eventbus.EventBus systemEventBus, final UIEventProvider eventProvider) { + this.executorService = executorService; this.eventBus = eventBus; this.systemEventBus = systemEventBus; this.eventProvider = eventProvider; @@ -100,7 +106,7 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { } if (!queue.offer(event)) { - LOG.warn("Deque limit is reached, cannot add more events!!! Dropped event is {}", event); + LOG.trace("Deque limit is reached, cannot add more events for UI {}! Dropped event is {}", uiid, event); return; } } @@ -112,7 +118,12 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { @Override public void init(final UI vaadinUI) { - LOG.debug("Initialize delayed event push strategy"); + uiid = vaadinUI.getUIId(); + LOG.info("Initialize delayed event push strategy for UI {}", uiid); + if (vaadinUI.getSession() == null) { + LOG.error("Vaadin session of UI {} is null! Event push disabled!", uiid); + } + jobHandle = executorService.scheduleWithFixedDelay(new DispatchRunnable(vaadinUI, vaadinUI.getSession()), 500, 2000, TimeUnit.MILLISECONDS); systemEventBus.register(this); @@ -120,10 +131,9 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { @Override public void clean() { - LOG.debug("Cleanup resources"); - jobHandle.cancel(true); + LOG.info("Cleanup delayed event push strategy for UI", uiid); systemEventBus.unregister(this); - executorService.shutdownNow(); + jobHandle.cancel(true); queue.clear(); } @@ -138,7 +148,7 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { * @return {@code true} if the event can be dispatched to the UI otherwise * {@code false} */ - protected boolean eventSecurityCheck(final SecurityContext userContext, + protected static boolean eventSecurityCheck(final SecurityContext userContext, final org.eclipse.hawkbit.eventbus.event.Event event) { if (userContext == null || userContext.getAuthentication() == null) { return false; @@ -163,27 +173,29 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { @Override public void run() { - LOG.debug("UI EventBus aggregator started"); + LOG.debug("UI EventBus aggregator started for UI {}", uiid); final long timestamp = System.currentTimeMillis(); - final List events = new LinkedList<>(); - for (int i = 0; i < BLOCK_SIZE; i++) { + + final int size = queue.size(); + if (size <= 0) { + LOG.debug("UI EventBus aggregator for UI {} has nothing to do.", uiid); + return; + } + + final List events = new ArrayList<>(size); + for (int i = 0; i < size; i++) { final org.eclipse.hawkbit.eventbus.event.Event pollEvent = queue.poll(); if (pollEvent == null) { continue; } - events.add(pollEvent); + events.add(i, pollEvent); } if (events.isEmpty()) { + LOG.debug("UI EventBus aggregator for UI {} has nothing to do.", uiid); return; } - if (vaadinSession == null) { - return; - } - - LOG.debug("UI EventBus aggregator session: {}", vaadinSession); - final WrappedSession wrappedSession = vaadinSession.getSession(); if (wrappedSession == null) { return; @@ -191,10 +203,13 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { final int eventsSize = events.size(); + LOG.debug("UI EventBus aggregator dispatches {} events for session {} for UI {}", eventsSize, vaadinSession, + uiid); + doDispatch(events, wrappedSession); - LOG.debug("UI EventBus aggregator done with sending {} events in {} ms", eventsSize, - System.currentTimeMillis() - timestamp); + LOG.debug("UI EventBus aggregator done with sending {} events in {} ms for UI {}", eventsSize, + System.currentTimeMillis() - timestamp, uiid); } @@ -210,9 +225,13 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { if (vaadinSession.getState() != State.OPEN) { return; } + LOG.debug("UI EventBus aggregator of UI {} got lock on session.", uiid); fowardSingleEvents(events, userContext); fowardBulkEvents(events, userContext); - }); + LOG.debug("UI EventBus aggregator of UI {} left lock on session.", uiid); + }).get(); + } catch (InterruptedException | ExecutionException e) { + LOG.error("Wait for Vaadin session for UI {} interrupted!", uiid, e); } finally { SecurityContextHolder.setContext(oldContext); } @@ -222,10 +241,7 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { final SecurityContext userContext) { final Set> filterBulkEvenTypes = eventProvider.getFilteredBulkEventsType(events); publishBulkEvent(events, userContext, filterBulkEvenTypes); - } - private void publishBulkEvent(final List events, - final SecurityContext userContext, final Set> filterBulkEvenTypes) { for (final Class bulkType : filterBulkEvenTypes) { final List listBulkEvents = events.stream() .filter(event -> DelayedEventBusPushStrategy.this.eventSecurityCheck(userContext, event) @@ -237,6 +253,11 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { } } + private void publishBulkEvent(final List events, + final SecurityContext userContext, final Set> filterBulkEvenTypes) { + + } + private void fowardSingleEvents(final List events, final SecurityContext userContext) { events.stream() diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rollout/RolloutListGrid.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rollout/RolloutListGrid.java index be7339324..7c423f758 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rollout/RolloutListGrid.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rollout/RolloutListGrid.java @@ -21,7 +21,6 @@ import static org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions.VAR_TOTAL_TARGET import java.util.ArrayList; import java.util.Arrays; import java.util.EnumMap; -import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -57,6 +56,7 @@ import org.vaadin.addons.lazyquerycontainer.LazyQueryDefinition; import org.vaadin.spring.events.EventScope; import org.vaadin.spring.events.annotation.EventBusListenerMethod; +import com.google.common.collect.Maps; import com.vaadin.data.Container; import com.vaadin.data.Item; import com.vaadin.data.util.converter.Converter; @@ -495,7 +495,8 @@ public class RolloutListGrid extends AbstractGrid { * Contains all expected rollout status per column to enable or disable * the button. */ - private static final Map EXPECTED_ROLLOUT_STATUS_ENABLE_BUTTON = new HashMap<>(); + private static final Map EXPECTED_ROLLOUT_STATUS_ENABLE_BUTTON = Maps + .newHashMapWithExpectedSize(2); private final Container.Indexed containerDataSource; static { diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/HawkbitCommonUtil.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/HawkbitCommonUtil.java index d8e93350b..105886fb2 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/HawkbitCommonUtil.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/HawkbitCommonUtil.java @@ -9,8 +9,8 @@ package org.eclipse.hawkbit.ui.utils; import java.util.Arrays; +import java.util.Collections; import java.util.Date; -import java.util.HashMap; import java.util.Map; import java.util.TimeZone; @@ -435,8 +435,7 @@ public final class HawkbitCommonUtil { */ public static LazyQueryContainer createLazyQueryContainer( final BeanQueryFactory> queryFactory) { - final Map queryConfig = new HashMap<>(); - queryFactory.setQueryConfiguration(queryConfig); + queryFactory.setQueryConfiguration(Collections.emptyMap()); return new LazyQueryContainer(new LazyQueryDefinition(true, 20, SPUILabelDefinitions.VAR_NAME), queryFactory); } @@ -448,8 +447,7 @@ public final class HawkbitCommonUtil { */ public static LazyQueryContainer createDSLazyQueryContainer( final BeanQueryFactory> queryFactory) { - final Map queryConfig = new HashMap<>(); - queryFactory.setQueryConfiguration(queryConfig); + queryFactory.setQueryConfiguration(Collections.emptyMap()); return new LazyQueryContainer(new LazyQueryDefinition(true, 20, "tagIdName"), queryFactory); } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPDateTimeUtil.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPDateTimeUtil.java index 49bae86e1..df8cf27d6 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPDateTimeUtil.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPDateTimeUtil.java @@ -11,7 +11,6 @@ package org.eclipse.hawkbit.ui.utils; import java.text.SimpleDateFormat; import java.time.ZoneId; import java.util.Date; -import java.util.HashMap; import java.util.Map; import java.util.TimeZone; @@ -19,6 +18,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.time.DurationFormatUtils; import org.eclipse.hawkbit.repository.model.BaseEntity; +import com.google.common.collect.Maps; import com.vaadin.server.WebBrowser; /** @@ -32,7 +32,7 @@ import com.vaadin.server.WebBrowser; public final class SPDateTimeUtil { private static final String DURATION_FORMAT = "y','M','d','H','m','s"; - private static final Map DURATION_I18N = new HashMap<>(); + private static final Map DURATION_I18N = Maps.newHashMapWithExpectedSize(6); static { DURATION_I18N.put(0, CalendarI18N.YEAR); From 03f94ab555064a3b64461b91347955a3717aca7a Mon Sep 17 00:00:00 2001 From: kaizimmerm Date: Tue, 20 Sep 2016 13:55:32 +0200 Subject: [PATCH 10/43] Fixed broken optimizations Signed-off-by: kaizimmerm --- .../model/rollout/MgmtRolloutResponseBody.java | 3 ++- .../jpa/JpaDistributionSetManagement.java | 14 +++++--------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/rollout/MgmtRolloutResponseBody.java b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/rollout/MgmtRolloutResponseBody.java index ce74b4c8d..79455f501 100644 --- a/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/rollout/MgmtRolloutResponseBody.java +++ b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/rollout/MgmtRolloutResponseBody.java @@ -8,6 +8,7 @@ */ package org.eclipse.hawkbit.mgmt.json.model.rollout; +import java.util.HashMap; import java.util.Map; import org.eclipse.hawkbit.mgmt.json.model.MgmtNamedEntity; @@ -37,7 +38,7 @@ public class MgmtRolloutResponseBody extends MgmtNamedEntity { private Long totalTargets; @JsonProperty(required = true) - private Map totalTargetsPerStatus; + private final Map totalTargetsPerStatus = new HashMap<>(); /** * @return the status diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetManagement.java index a4295cdae..994c880e4 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetManagement.java @@ -15,7 +15,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; -import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -67,6 +66,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import com.google.common.base.Strings; +import com.google.common.collect.Lists; import com.google.common.eventbus.EventBus; /** @@ -347,7 +347,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { final Specification spec = RSQLUtility.parse(rsqlParam, DistributionSetFields.class); - final List> specList = new LinkedList<>(); + final List> specList = new ArrayList<>(2); if (deleted != null) { specList.add(DistributionSetSpecification.isDeleted(deleted)); } @@ -377,7 +377,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { final Page findDistributionSetsByFilters = findDistributionSetsByFilters(pageable, dsFilterWithNoTargetLinked); - final List resultSet = new LinkedList<>(findDistributionSetsByFilters.getContent()); + final List resultSet = new ArrayList<>(findDistributionSetsByFilters.getContent()); int orderIndex = 0; if (installedDS != null) { final boolean remove = resultSet.remove(installedDS); @@ -414,13 +414,9 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { @Override public Long countDistributionSetsAll() { - - final List> specList = new LinkedList<>(); - final Specification spec = DistributionSetSpecification.isDeleted(Boolean.FALSE); - specList.add(spec); - return distributionSetRepository.count(SpecificationsBuilder.combineWithAnd(specList)); + return distributionSetRepository.count(SpecificationsBuilder.combineWithAnd(Lists.newArrayList(spec))); } @Override @@ -599,7 +595,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { private static List> buildDistributionSetSpecifications( final DistributionSetFilter distributionSetFilter) { - final List> specList = new LinkedList<>(); + final List> specList = new ArrayList<>(7); Specification spec; From 0ff02c88788ad689f291c91a5e22c0451b53f8b3 Mon Sep 17 00:00:00 2001 From: kaizimmerm Date: Tue, 20 Sep 2016 14:10:47 +0200 Subject: [PATCH 11/43] Reduced empty collections Signed-off-by: kaizimmerm --- .../org/eclipse/hawkbit/ui/common/CommonDialogWindow.java | 4 ++-- .../footer/ManangementConfirmationWindowLayout.java | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/CommonDialogWindow.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/CommonDialogWindow.java index ba9de4539..b1491fe53 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/CommonDialogWindow.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/CommonDialogWindow.java @@ -12,7 +12,6 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -34,6 +33,7 @@ import org.vaadin.hene.flexibleoptiongroup.FlexibleOptionGroupItemComponent; import com.google.common.base.Strings; import com.google.common.collect.Sets; +import com.google.gwt.thirdparty.guava.common.collect.Maps; import com.vaadin.data.Container.ItemSetChangeEvent; import com.vaadin.data.Container.ItemSetChangeListener; import com.vaadin.data.Property.ValueChangeEvent; @@ -129,8 +129,8 @@ public class CommonDialogWindow extends Window { this.helpLink = helpLink; this.closeListener = closeListener; this.cancelButtonClickListener = cancelButtonClickListener; - this.orginalValues = new HashMap<>(); this.allComponents = getAllComponents(layout); + this.orginalValues = Maps.newHashMapWithExpectedSize(allComponents.size()); this.i18n = i18n; init(); } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/footer/ManangementConfirmationWindowLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/footer/ManangementConfirmationWindowLayout.java index 4524f6d9c..a3fd172a9 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/footer/ManangementConfirmationWindowLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/footer/ManangementConfirmationWindowLayout.java @@ -10,7 +10,6 @@ package org.eclipse.hawkbit.ui.management.footer; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -151,7 +150,7 @@ public class ManangementConfirmationWindowLayout extends AbstractConfirmationWin ? actionTypeOptionGroupLayout.getForcedTimeDateField().getValue().getTime() : RepositoryModelConstants.NO_FORCE_TIME; - final Map> saveAssignedList = new HashMap<>(); + final Map> saveAssignedList = Maps.newHashMapWithExpectedSize(itemIds.size()); int successAssignmentCount = 0; int duplicateAssignmentCount = 0; From b7199bff724807892d1415a0177bc5d3f56fb0e5 Mon Sep 17 00:00:00 2001 From: kaizimmerm Date: Tue, 20 Sep 2016 14:34:27 +0200 Subject: [PATCH 12/43] Fixed broken renderer. Signed-off-by: kaizimmerm --- .../client/renderers/HtmlLabelRenderer.java | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/customrenderers/client/renderers/HtmlLabelRenderer.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/customrenderers/client/renderers/HtmlLabelRenderer.java index 535150f04..c6b2d9b64 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/customrenderers/client/renderers/HtmlLabelRenderer.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/customrenderers/client/renderers/HtmlLabelRenderer.java @@ -8,9 +8,9 @@ */ package org.eclipse.hawkbit.ui.customrenderers.client.renderers; +import java.util.HashMap; import java.util.Map; -import com.google.common.collect.Maps; import com.google.gwt.core.client.GWT; import com.vaadin.client.renderers.WidgetRenderer; import com.vaadin.client.ui.VLabel; @@ -29,11 +29,11 @@ public class HtmlLabelRenderer extends WidgetRenderer { } @Override - public void render(final RendererCellReference cell, final String input, final VLabel label) { - final Map map = formatInput(input); - final String value = map.containsKey("value") ? map.get("value") : null; - final String style = map.containsKey("style") ? map.get("style") : null; - final String id = map.containsKey("id") ? map.get("id") : null; + public void render(RendererCellReference cell, String input, VLabel label) { + Map map = formatInput(input); + String value = map.containsKey("value") ? map.get("value") : null; + String style = map.containsKey("style") ? map.get("style") : null; + String id = map.containsKey("id") ? map.get("id") : null; if (value != null) { label.setHTML("&#x" + Integer.toHexString(Integer.parseInt(value)) + ";"); @@ -44,7 +44,7 @@ public class HtmlLabelRenderer extends WidgetRenderer { label.getElement().setId(id); } - private void applyStyle(final VLabel label, final String style) { + private void applyStyle(VLabel label, String style) { label.setStyleName(VLabel.CLASSNAME); label.addStyleName(getStyle("small")); label.addStyleName(getStyle("font-icon")); @@ -57,12 +57,11 @@ public class HtmlLabelRenderer extends WidgetRenderer { return new StringBuilder(style).append(" ").append(VLabel.CLASSNAME).append("-").append(style).toString(); } - private Map formatInput(final String input) { - - final String[] tempData = input.split(","); - final Map details = Maps.newHashMapWithExpectedSize(tempData.length); - for (final String statusWithCount : tempData) { - final String[] statusWithCountList = statusWithCount.split(":"); + private Map formatInput(String input) { + Map details = new HashMap<>(); + String[] tempData = input.split(","); + for (String statusWithCount : tempData) { + String[] statusWithCountList = statusWithCount.split(":"); details.put(statusWithCountList[0], statusWithCountList[1]); } return details; From 1000ca3d19299b3f7be44d74661cc296b941b4f0 Mon Sep 17 00:00:00 2001 From: kaizimmerm Date: Tue, 20 Sep 2016 18:30:31 +0200 Subject: [PATCH 13/43] Fixed import and sonar issues Signed-off-by: kaizimmerm --- .../simulator/amqp/AmqpConfiguration.java | 2 +- .../repository/jpa/model/JpaActionStatus.java | 9 ++++--- .../jpa/model/JpaDistributionSet.java | 24 +++++++++++-------- .../jpa/model/JpaDistributionSetType.java | 18 ++++++++++---- .../jpa/model/JpaSoftwareModule.java | 5 ++-- .../repository/jpa/model/JpaTarget.java | 13 +++++++--- .../event/UploadViewAcceptCriteria.java | 2 +- .../UploadViewConfirmationWindowLayout.java | 4 ++-- .../smtable/SoftwareModuleTable.java | 4 ++-- .../hawkbit/ui/common/CommonDialogWindow.java | 4 ++-- .../ui/common/table/AbstractTable.java | 4 ++-- .../ui/push/DelayedEventBusPushStrategy.java | 10 ++------ 12 files changed, 56 insertions(+), 43 deletions(-) diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/AmqpConfiguration.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/AmqpConfiguration.java index f024f174b..6a8826c82 100644 --- a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/AmqpConfiguration.java +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/AmqpConfiguration.java @@ -35,7 +35,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.retry.backoff.ExponentialBackOffPolicy; import org.springframework.retry.support.RetryTemplate; -import com.google.gwt.thirdparty.guava.common.collect.Maps; +import com.google.common.collect.Maps; /** * The spring AMQP configuration to use a AMQP for communication with SP update diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaActionStatus.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaActionStatus.java index f442f552a..061351053 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaActionStatus.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaActionStatus.java @@ -8,8 +8,8 @@ */ package org.eclipse.hawkbit.repository.jpa.model; +import java.util.ArrayList; import java.util.Collections; -import java.util.LinkedList; import java.util.List; import javax.persistence.CollectionTable; @@ -136,11 +136,10 @@ public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements @Override public final void addMessage(final String message) { - if (messages == null) { - messages = new LinkedList<>(); - } - if (message != null) { + if (messages == null) { + messages = new ArrayList<>((message.length() / 512) + 1); + } Splitter.fixedLength(512).split(message).forEach(messages::add); } } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSet.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSet.java index 349d8f7e7..8e12967c5 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSet.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSet.java @@ -119,7 +119,7 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen * Default constructor. */ public JpaDistributionSet() { - super(); + // Default constructor for JPA } /** @@ -252,15 +252,7 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen modules = new HashSet<>(); } - // 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(); - } + checkTypeCompatability(softwareModule); final Optional found = modules.stream() .filter(module -> module.getId().equals(softwareModule.getId())).findFirst(); @@ -286,6 +278,18 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen return false; } + private void checkTypeCompatability(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(); + } + } + @Override public boolean removeModule(final SoftwareModule softwareModule) { if (modules == null) { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSetType.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSetType.java index e7bc9d347..44bed8cce 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSetType.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSetType.java @@ -130,18 +130,26 @@ public class JpaDistributionSetType extends AbstractJpaNamedEntity implements Di @Override public boolean areModuleEntriesIdentical(final DistributionSetType dsType) { - if (!(dsType instanceof JpaDistributionSetType)) { + if (!(dsType instanceof JpaDistributionSetType) || isOneModuleListEmpty(dsType)) { return false; - } else if (CollectionUtils.isEmpty(elements) - && CollectionUtils.isEmpty(((JpaDistributionSetType) dsType).elements)) { + } else if (areBothModuleListsEmpty(dsType)) { return true; - } else if (CollectionUtils.isEmpty(elements)) { - return false; } return new HashSet(((JpaDistributionSetType) dsType).elements).equals(elements); } + private boolean isOneModuleListEmpty(final DistributionSetType dsType) { + return (!CollectionUtils.isEmpty(((JpaDistributionSetType) dsType).elements) + && CollectionUtils.isEmpty(elements)) + || (CollectionUtils.isEmpty(((JpaDistributionSetType) dsType).elements) + && !CollectionUtils.isEmpty(elements)); + } + + private boolean areBothModuleListsEmpty(final DistributionSetType dsType) { + return CollectionUtils.isEmpty(((JpaDistributionSetType) dsType).elements) && CollectionUtils.isEmpty(elements); + } + @Override public DistributionSetType addOptionalModuleType(final SoftwareModuleType smType) { if (elements == null) { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java index 49ff030d4..86b64fd31 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java @@ -8,6 +8,7 @@ */ package org.eclipse.hawkbit.repository.jpa.model; +import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -90,7 +91,7 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement * Default constructor. */ public JpaSoftwareModule() { - + // Default constructor for JPA } /** @@ -121,7 +122,7 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement @Override public void addArtifact(final LocalArtifact artifact) { if (null == artifacts) { - artifacts = new LinkedList<>(); + artifacts = new ArrayList<>(4); artifacts.add(artifact); return; } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTarget.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTarget.java index 7756c947e..4b0041a00 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTarget.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTarget.java @@ -11,7 +11,6 @@ package org.eclipse.hawkbit.repository.jpa.model; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; -import java.util.LinkedList; import java.util.List; import java.util.Set; @@ -116,7 +115,7 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable rolloutTargetGroup = new ArrayList<>(); + private List rolloutTargetGroup; /** * Constructor. @@ -166,6 +165,14 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable getRolloutTargetGroup() { + if (rolloutTargetGroup == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(rolloutTargetGroup); + } + public boolean addTag(final TargetTag tag) { if (tags == null) { tags = new HashSet<>(); @@ -201,7 +208,7 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable(); + actions = new ArrayList<>(4); } return actions.add(action); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/event/UploadViewAcceptCriteria.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/event/UploadViewAcceptCriteria.java index 02c3ed729..2fabb3bb9 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/event/UploadViewAcceptCriteria.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/event/UploadViewAcceptCriteria.java @@ -15,7 +15,7 @@ import java.util.Map; import org.eclipse.hawkbit.ui.common.AbstractAcceptCriteria; import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; -import com.google.gwt.thirdparty.guava.common.collect.Maps; +import com.google.common.collect.Maps; import com.vaadin.spring.annotation.SpringComponent; import com.vaadin.spring.annotation.ViewScope; import com.vaadin.ui.Component; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/footer/UploadViewConfirmationWindowLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/footer/UploadViewConfirmationWindowLayout.java index 52698b3e5..3d875e450 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/footer/UploadViewConfirmationWindowLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/footer/UploadViewConfirmationWindowLayout.java @@ -20,12 +20,12 @@ import org.eclipse.hawkbit.ui.artifacts.state.CustomFile; import org.eclipse.hawkbit.ui.common.confirmwindow.layout.AbstractConfirmationWindowLayout; import org.eclipse.hawkbit.ui.common.confirmwindow.layout.ConfirmationTab; import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil; -import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.springframework.beans.factory.annotation.Autowired; -import com.google.gwt.thirdparty.guava.common.collect.Maps; +import com.google.common.collect.Maps; import com.vaadin.data.Container; import com.vaadin.data.Item; import com.vaadin.data.util.IndexedContainer; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleTable.java index 5906ec573..0168ad3d9 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleTable.java @@ -24,11 +24,11 @@ import org.eclipse.hawkbit.ui.components.SPUIComponentProvider; import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleSmallNoBorder; import org.eclipse.hawkbit.ui.distributions.smtable.SwMetadataPopupLayout; import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil; -import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions; import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions; import org.eclipse.hawkbit.ui.utils.TableColumn; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.springframework.beans.factory.annotation.Autowired; import org.vaadin.addons.lazyquerycontainer.BeanQueryFactory; import org.vaadin.addons.lazyquerycontainer.LazyQueryContainer; @@ -36,7 +36,7 @@ import org.vaadin.addons.lazyquerycontainer.LazyQueryDefinition; import org.vaadin.spring.events.EventScope; import org.vaadin.spring.events.annotation.EventBusListenerMethod; -import com.google.gwt.thirdparty.guava.common.collect.Maps; +import com.google.common.collect.Maps; import com.vaadin.data.Container; import com.vaadin.data.Item; import com.vaadin.event.dd.DragAndDropEvent; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/CommonDialogWindow.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/CommonDialogWindow.java index 3ab3bf536..3cc00e016 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/CommonDialogWindow.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/CommonDialogWindow.java @@ -27,13 +27,13 @@ import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleNoBorderWithIcon; import org.eclipse.hawkbit.ui.layouts.AbstractCreateUpdateTagLayout; import org.eclipse.hawkbit.ui.management.targettable.TargetAddUpdateWindowLayout; import org.eclipse.hawkbit.ui.utils.I18N; -import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.vaadin.hene.flexibleoptiongroup.FlexibleOptionGroupItemComponent; import com.google.common.base.Strings; +import com.google.common.collect.Maps; import com.google.common.collect.Sets; -import com.google.gwt.thirdparty.guava.common.collect.Maps; import com.vaadin.data.Container.ItemSetChangeEvent; import com.vaadin.data.Container.ItemSetChangeListener; import com.vaadin.data.Property.ValueChangeEvent; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/table/AbstractTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/table/AbstractTable.java index fc560c2fa..e34964bb4 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/table/AbstractTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/table/AbstractTable.java @@ -30,8 +30,8 @@ import org.eclipse.hawkbit.ui.utils.UINotification; import org.springframework.beans.factory.annotation.Autowired; import org.vaadin.spring.events.EventBus; -import com.google.gwt.thirdparty.guava.common.collect.Iterables; -import com.google.gwt.thirdparty.guava.common.collect.Sets; +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; import com.vaadin.data.Container; import com.vaadin.data.Item; import com.vaadin.event.Transferable; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/push/DelayedEventBusPushStrategy.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/push/DelayedEventBusPushStrategy.java index bbb9335a3..0fdb06a46 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/push/DelayedEventBusPushStrategy.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/push/DelayedEventBusPushStrategy.java @@ -240,11 +240,10 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { private void fowardBulkEvents(final List events, final SecurityContext userContext) { final Set> filterBulkEvenTypes = eventProvider.getFilteredBulkEventsType(events); - publishBulkEvent(events, userContext, filterBulkEvenTypes); for (final Class bulkType : filterBulkEvenTypes) { final List listBulkEvents = events.stream() - .filter(event -> DelayedEventBusPushStrategy.this.eventSecurityCheck(userContext, event) + .filter(event -> DelayedEventBusPushStrategy.eventSecurityCheck(userContext, event) && bulkType.isInstance(event)) .collect(Collectors.toList()); if (!listBulkEvents.isEmpty()) { @@ -253,15 +252,10 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { } } - private void publishBulkEvent(final List events, - final SecurityContext userContext, final Set> filterBulkEvenTypes) { - - } - private void fowardSingleEvents(final List events, final SecurityContext userContext) { events.stream() - .filter(event -> DelayedEventBusPushStrategy.this.eventSecurityCheck(userContext, event) + .filter(event -> DelayedEventBusPushStrategy.eventSecurityCheck(userContext, event) && eventProvider.getSingleEvents().contains(event.getClass())) .forEach(event -> eventBus.publish(vaadinUI, event)); } From b4765da3c913551cb4061817bd9f87dcb931cc67 Mon Sep 17 00:00:00 2001 From: Dominic Schabel Date: Wed, 21 Sep 2016 14:45:32 +0200 Subject: [PATCH 14/43] Used builder for ResponseEntity + minor clean-up Signed-off-by: Dominic Schabel --- .../resource/MgmtSoftwareModuleResource.java | 72 +++++++++++-------- 1 file changed, 42 insertions(+), 30 deletions(-) diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResource.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResource.java index f3759d94f..ae099dcf6 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResource.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResource.java @@ -8,6 +8,13 @@ */ package org.eclipse.hawkbit.mgmt.rest.resource; +import static org.eclipse.hawkbit.mgmt.rest.resource.MgmtSoftwareModuleMapper.artifactsToResponse; +import static org.eclipse.hawkbit.mgmt.rest.resource.MgmtSoftwareModuleMapper.toResponse; +import static org.eclipse.hawkbit.mgmt.rest.resource.MgmtSoftwareModuleMapper.toResponseSwMetadata; +import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpStatus.CREATED; +import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; + import java.io.IOException; import java.util.Collection; import java.util.List; @@ -47,10 +54,10 @@ import org.springframework.web.multipart.MultipartFile; /** * REST Resource handling for {@link SoftwareModule} and related * {@link Artifact} CRUD operations. - * */ @RestController public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi { + private static final Logger LOG = LoggerFactory.getLogger(MgmtSoftwareModuleResource.class); @Autowired @@ -70,7 +77,7 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi { @RequestParam(value = "sha1sum", required = false) final String sha1Sum) { if (file.isEmpty()) { - return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + return new ResponseEntity<>(BAD_REQUEST); } String fileName = optionalFileName; @@ -82,10 +89,10 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi { final Artifact result = artifactManagement.createLocalArtifact(file.getInputStream(), softwareModuleId, fileName, md5Sum == null ? null : md5Sum.toLowerCase(), sha1Sum == null ? null : sha1Sum.toLowerCase(), false, file.getContentType()); - return new ResponseEntity<>(MgmtSoftwareModuleMapper.toResponse(result), HttpStatus.CREATED); + return ResponseEntity.status(CREATED).body(toResponse(result)); } catch (final IOException e) { LOG.error("Failed to store artifact", e); - return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + return new ResponseEntity<>(INTERNAL_SERVER_ERROR); } } @@ -93,31 +100,29 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi { @ResponseBody public ResponseEntity> getArtifacts( @PathVariable("softwareModuleId") final Long softwareModuleId) { - final SoftwareModule module = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null); - return new ResponseEntity<>(MgmtSoftwareModuleMapper.artifactsToResponse(module.getArtifacts()), HttpStatus.OK); + final SoftwareModule module = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null); + return ResponseEntity.ok(artifactsToResponse(module.getArtifacts())); } @Override @ResponseBody public ResponseEntity getArtifact(@PathVariable("softwareModuleId") final Long softwareModuleId, @PathVariable("artifactId") final Long artifactId) { - final SoftwareModule module = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, artifactId); - return new ResponseEntity<>(MgmtSoftwareModuleMapper.toResponse(module.getLocalArtifact(artifactId).get()), - HttpStatus.OK); + final SoftwareModule module = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, artifactId); + return ResponseEntity.ok(toResponse(module.getLocalArtifact(artifactId).get())); } @Override @ResponseBody public ResponseEntity deleteArtifact(@PathVariable("softwareModuleId") final Long softwareModuleId, @PathVariable("artifactId") final Long artifactId) { - findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, artifactId); + findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, artifactId); artifactManagement.deleteLocalArtifact(artifactId); - return new ResponseEntity<>(HttpStatus.OK); - + return ResponseEntity.ok().build(); } @Override @@ -144,32 +149,34 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi { } final List rest = MgmtSoftwareModuleMapper.toResponse(findModulesAll.getContent()); - return new ResponseEntity<>(new PagedList<>(rest, countModulesAll), HttpStatus.OK); + return ResponseEntity.ok(new PagedList<>(rest, countModulesAll)); } @Override public ResponseEntity getSoftwareModule( @PathVariable("softwareModuleId") final Long softwareModuleId) { - final SoftwareModule findBaseSoftareModule = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null); - return new ResponseEntity<>(MgmtSoftwareModuleMapper.toResponse(findBaseSoftareModule), HttpStatus.OK); + final SoftwareModule findBaseSoftareModule = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null); + return ResponseEntity.ok(toResponse(findBaseSoftareModule)); } @Override public ResponseEntity> createSoftwareModules( @RequestBody final List softwareModules) { + LOG.debug("creating {} softwareModules", softwareModules.size()); final Collection createdSoftwareModules = softwareManagement.createSoftwareModule( MgmtSoftwareModuleMapper.smFromRequest(entityFactory, softwareModules, softwareManagement)); LOG.debug("{} softwareModules created, return status {}", softwareModules.size(), HttpStatus.CREATED); - return new ResponseEntity<>(MgmtSoftwareModuleMapper.toResponse(createdSoftwareModules), HttpStatus.CREATED); + return ResponseEntity.status(CREATED).body(toResponse(createdSoftwareModules)); } @Override public ResponseEntity updateSoftwareModule( @PathVariable("softwareModuleId") final Long softwareModuleId, @RequestBody final MgmtSoftwareModuleRequestBodyPut restSoftwareModule) { + final SoftwareModule module = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null); // only description and vendor can be modified @@ -181,16 +188,16 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi { } final SoftwareModule updateSoftwareModule = softwareManagement.updateSoftwareModule(module); - return new ResponseEntity<>(MgmtSoftwareModuleMapper.toResponse(updateSoftwareModule), HttpStatus.OK); + return ResponseEntity.ok(toResponse(updateSoftwareModule)); } @Override public ResponseEntity deleteSoftwareModule(@PathVariable("softwareModuleId") final Long softwareModuleId) { - final SoftwareModule module = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null); + final SoftwareModule module = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null); softwareManagement.deleteSoftwareModule(module); - return new ResponseEntity<>(HttpStatus.OK); + return ResponseEntity.ok().build(); } @Override @@ -218,34 +225,38 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi { metaDataPage = softwareManagement.findSoftwareModuleMetadataBySoftwareModuleId(softwareModuleId, pageable); } - return new ResponseEntity<>( - new PagedList<>(MgmtSoftwareModuleMapper.toResponseSwMetadata(metaDataPage.getContent()), - metaDataPage.getTotalElements()), - HttpStatus.OK); + return ResponseEntity + .ok(new PagedList<>(toResponseSwMetadata(metaDataPage.getContent()), metaDataPage.getTotalElements())); } @Override public ResponseEntity getMetadataValue(@PathVariable("softwareModuleId") final Long softwareModuleId, @PathVariable("metadataKey") final String metadataKey) { + final SoftwareModule sw = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null); final SoftwareModuleMetadata findOne = softwareManagement.findSoftwareModuleMetadata(sw, metadataKey); - return ResponseEntity. ok(MgmtSoftwareModuleMapper.toResponseSwMetadata(findOne)); + + return ResponseEntity.ok(toResponseSwMetadata(findOne)); } @Override public ResponseEntity updateMetadata(@PathVariable("softwareModuleId") final Long softwareModuleId, @PathVariable("metadataKey") final String metadataKey, @RequestBody final MgmtMetadata metadata) { + final SoftwareModule sw = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null); final SoftwareModuleMetadata updated = softwareManagement.updateSoftwareModuleMetadata( entityFactory.generateSoftwareModuleMetadata(sw, metadataKey, metadata.getValue())); - return ResponseEntity.ok(MgmtSoftwareModuleMapper.toResponseSwMetadata(updated)); + + return ResponseEntity.ok(toResponseSwMetadata(updated)); } @Override public ResponseEntity deleteMetadata(@PathVariable("softwareModuleId") final Long softwareModuleId, @PathVariable("metadataKey") final String metadataKey) { + final SoftwareModule sw = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null); softwareManagement.deleteSoftwareModuleMetadata(sw, metadataKey); + return ResponseEntity.ok().build(); } @@ -253,24 +264,25 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi { public ResponseEntity> createMetadata( @PathVariable("softwareModuleId") final Long softwareModuleId, @RequestBody final List metadataRest) { - final SoftwareModule sw = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null); + final SoftwareModule sw = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null); final List created = softwareManagement.createSoftwareModuleMetadata( MgmtSoftwareModuleMapper.fromRequestSwMetadata(entityFactory, sw, metadataRest)); - return new ResponseEntity<>(MgmtSoftwareModuleMapper.toResponseSwMetadata(created), HttpStatus.CREATED); - + return ResponseEntity.status(CREATED).body(toResponseSwMetadata(created)); } private SoftwareModule findSoftwareModuleWithExceptionIfNotFound(final Long softwareModuleId, final Long artifactId) { + final SoftwareModule module = softwareManagement.findSoftwareModuleById(softwareModuleId); if (module == null) { throw new EntityNotFoundException("SoftwareModule with Id {" + softwareModuleId + "} does not exist"); - } else if (artifactId != null && !module.getLocalArtifact(artifactId).isPresent()) { + } + if (artifactId != null && !module.getLocalArtifact(artifactId).isPresent()) { throw new EntityNotFoundException("Artifact with Id {" + artifactId + "} does not exist"); } + return module; } - } From da22bc4ad09ecd04b9154e34a3f2fbcfbeea1ac9 Mon Sep 17 00:00:00 2001 From: Dominic Schabel Date: Wed, 21 Sep 2016 15:14:34 +0200 Subject: [PATCH 15/43] Used builder for ResponseEntity within this class as well Signed-off-by: Dominic Schabel --- .../MgmtDistributionSetTypeResource.java | 63 ++++++++----------- 1 file changed, 27 insertions(+), 36 deletions(-) diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeResource.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeResource.java index 762e0e104..fe62ff369 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeResource.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeResource.java @@ -8,6 +8,12 @@ */ package org.eclipse.hawkbit.mgmt.rest.resource; +import static org.eclipse.hawkbit.mgmt.rest.resource.MgmtDistributionSetTypeMapper.toResponse; +import static org.eclipse.hawkbit.mgmt.rest.resource.MgmtDistributionSetTypeMapper.toTypesResponse; +import static org.eclipse.hawkbit.mgmt.rest.resource.MgmtSoftwareModuleTypeMapper.toResponse; +import static org.eclipse.hawkbit.mgmt.rest.resource.MgmtSoftwareModuleTypeMapper.toTypesResponse; +import static org.springframework.http.HttpStatus.CREATED; + import java.util.List; import org.eclipse.hawkbit.mgmt.json.model.MgmtId; @@ -32,7 +38,6 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.domain.Sort; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; @@ -42,8 +47,6 @@ import org.springframework.web.bind.annotation.RestController; /** * REST Resource handling for {@link SoftwareModule} and related * {@link Artifact} CRUD operations. - * - * */ @RestController public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeRestApi { @@ -67,7 +70,6 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR final int sanitizedOffsetParam = PagingUtility.sanitizeOffsetParam(pagingOffsetParam); final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam); final Sort sorting = PagingUtility.sanitizeDistributionSetTypeSortParam(sortParam); - final Pageable pageable = new OffsetBasedPageRequest(sanitizedOffsetParam, sanitizedLimitParam, sorting); final Slice findModuleTypessAll; @@ -82,31 +84,32 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR final List rest = MgmtDistributionSetTypeMapper .toListResponse(findModuleTypessAll.getContent()); - return new ResponseEntity<>(new PagedList<>(rest, countModulesAll), HttpStatus.OK); + return ResponseEntity.ok(new PagedList<>(rest, countModulesAll)); } @Override public ResponseEntity getDistributionSetType( @PathVariable("distributionSetTypeId") final Long distributionSetTypeId) { - final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId); - return new ResponseEntity<>(MgmtDistributionSetTypeMapper.toResponse(foundType), HttpStatus.OK); + final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId); + return ResponseEntity.ok(toResponse(foundType)); } @Override public ResponseEntity deleteDistributionSetType( @PathVariable("distributionSetTypeId") final Long distributionSetTypeId) { - final DistributionSetType module = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId); + final DistributionSetType module = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId); distributionSetManagement.deleteDistributionSetType(module); - return new ResponseEntity<>(HttpStatus.OK); + return ResponseEntity.ok().build(); } @Override public ResponseEntity updateDistributionSetType( @PathVariable("distributionSetTypeId") final Long distributionSetTypeId, @RequestBody final MgmtDistributionSetTypeRequestBodyPut restDistributionSetType) { + final DistributionSetType type = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId); // only description can be modified @@ -117,8 +120,7 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR final DistributionSetType updatedDistributionSetType = distributionSetManagement .updateDistributionSetType(type); - return new ResponseEntity<>(MgmtDistributionSetTypeMapper.toResponse(updatedDistributionSetType), - HttpStatus.OK); + return ResponseEntity.ok(toResponse(updatedDistributionSetType)); } @Override @@ -128,16 +130,17 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR final List createdSoftwareModules = distributionSetManagement.createDistributionSetTypes( MgmtDistributionSetTypeMapper.smFromRequest(entityFactory, softwareManagement, distributionSetTypes)); - return new ResponseEntity<>(MgmtDistributionSetTypeMapper.toTypesResponse(createdSoftwareModules), - HttpStatus.CREATED); + return ResponseEntity.status(CREATED).body(toTypesResponse(createdSoftwareModules)); } private DistributionSetType findDistributionSetTypeWithExceptionIfNotFound(final Long distributionSetTypeId) { + final DistributionSetType module = distributionSetManagement.findDistributionSetTypeById(distributionSetTypeId); if (module == null) { throw new EntityNotFoundException( "DistributionSetType with Id {" + distributionSetTypeId + "} does not exist"); } + return module; } @@ -146,8 +149,7 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR @PathVariable("distributionSetTypeId") final Long distributionSetTypeId) { final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId); - return new ResponseEntity<>(MgmtSoftwareModuleTypeMapper.toTypesResponse(foundType.getMandatoryModuleTypes()), - HttpStatus.OK); + return ResponseEntity.ok(toTypesResponse(foundType.getMandatoryModuleTypes())); } @Override @@ -156,7 +158,6 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR @PathVariable("softwareModuleTypeId") final Long softwareModuleTypeId) { final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId); - final SoftwareModuleType foundSmType = findSoftwareModuleTypeWithExceptionIfNotFound(softwareModuleTypeId); if (!foundType.containsMandatoryModuleType(foundSmType)) { @@ -164,7 +165,7 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR "Software module with given ID is not part of this distribution set type!"); } - return new ResponseEntity<>(MgmtSoftwareModuleTypeMapper.toResponse(foundSmType), HttpStatus.OK); + return ResponseEntity.ok(toResponse(foundSmType)); } @Override @@ -173,7 +174,6 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR @PathVariable("softwareModuleTypeId") final Long softwareModuleTypeId) { final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId); - final SoftwareModuleType foundSmType = findSoftwareModuleTypeWithExceptionIfNotFound(softwareModuleTypeId); if (!foundType.containsOptionalModuleType(foundSmType)) { @@ -181,7 +181,7 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR "Software module with given ID is not part of this distribution set type!"); } - return new ResponseEntity<>(MgmtSoftwareModuleTypeMapper.toResponse(foundSmType), HttpStatus.OK); + return ResponseEntity.ok(toResponse(foundSmType)); } @Override @@ -189,9 +189,7 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR @PathVariable("distributionSetTypeId") final Long distributionSetTypeId) { final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId); - - return new ResponseEntity<>(MgmtSoftwareModuleTypeMapper.toTypesResponse(foundType.getOptionalModuleTypes()), - HttpStatus.OK); + return ResponseEntity.ok(toTypesResponse(foundType.getOptionalModuleTypes())); } @Override @@ -200,7 +198,6 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR @PathVariable("softwareModuleTypeId") final Long softwareModuleTypeId) { final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId); - final SoftwareModuleType foundSmType = findSoftwareModuleTypeWithExceptionIfNotFound(softwareModuleTypeId); if (!foundType.containsMandatoryModuleType(foundSmType)) { @@ -209,18 +206,17 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR } foundType.removeModuleType(softwareModuleTypeId); - distributionSetManagement.updateDistributionSetType(foundType); - return new ResponseEntity<>(HttpStatus.OK); + return ResponseEntity.ok().build(); } @Override public ResponseEntity removeOptionalModule( @PathVariable("distributionSetTypeId") final Long distributionSetTypeId, @PathVariable("softwareModuleTypeId") final Long softwareModuleTypeId) { - final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId); + final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId); final SoftwareModuleType foundSmType = findSoftwareModuleTypeWithExceptionIfNotFound(softwareModuleTypeId); if (!foundType.containsOptionalModuleType(foundSmType)) { @@ -229,10 +225,9 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR } foundType.removeModuleType(softwareModuleTypeId); - distributionSetManagement.updateDistributionSetType(foundType); - return new ResponseEntity<>(HttpStatus.OK); + return ResponseEntity.ok().build(); } @Override @@ -240,14 +235,11 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR @PathVariable("distributionSetTypeId") final Long distributionSetTypeId, @RequestBody final MgmtId smtId) { final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId); - final SoftwareModuleType smType = findSoftwareModuleTypeWithExceptionIfNotFound(smtId.getId()); - foundType.addMandatoryModuleType(smType); - distributionSetManagement.updateDistributionSetType(foundType); - return new ResponseEntity<>(HttpStatus.OK); + return ResponseEntity.ok().build(); } @Override @@ -255,23 +247,22 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR @PathVariable("distributionSetTypeId") final Long distributionSetTypeId, @RequestBody final MgmtId smtId) { final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId); - final SoftwareModuleType smType = findSoftwareModuleTypeWithExceptionIfNotFound(smtId.getId()); - foundType.addOptionalModuleType(smType); distributionSetManagement.updateDistributionSetType(foundType); - return new ResponseEntity<>(HttpStatus.OK); - + return ResponseEntity.ok().build(); } private SoftwareModuleType findSoftwareModuleTypeWithExceptionIfNotFound(final Long softwareModuleTypeId) { + final SoftwareModuleType module = softwareManagement.findSoftwareModuleTypeById(softwareModuleTypeId); if (module == null) { throw new EntityNotFoundException( "SoftwareModuleType with Id {" + softwareModuleTypeId + "} does not exist"); } + return module; } } From ae583e49380fbdb7f41cf05db8fde5e592507f6d Mon Sep 17 00:00:00 2001 From: Jonathan Philip Knoblauch Date: Wed, 21 Sep 2016 17:47:19 +0200 Subject: [PATCH 16/43] Refactoring and adapted CSS styling to HawkBit Signed-off-by: Jonathan Philip Knoblauch --- .../org/eclipse/hawkbit/repository/rsql/SuggestToken.java | 7 ++----- .../repository/jpa/rsql/ParseExceptionWrapper.java | 3 ++- .../repository/jpa/rsql/RsqlParserValidationOracle.java | 4 +++- .../ui/filtermanagement/CreateOrUpdateFilterHeader.java | 8 +------- .../themes/hawkbit/customstyles/target-filter-query.scss | 2 +- 5 files changed, 9 insertions(+), 15 deletions(-) diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/rsql/SuggestToken.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/rsql/SuggestToken.java index 55f009adf..db560f17f 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/rsql/SuggestToken.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/rsql/SuggestToken.java @@ -26,10 +26,6 @@ public class SuggestToken { * the character position of the start of the token * @param end * the character position of the end of the token - * @param the - * token image name which is currently parsed, e.g. entered image - * name is {@code na} which the suggestion can be based on and - * e.g. filter the suggestions. * @param tokenImageName * the entered name of the token, e.g. could be the beginning of * the suggestion like 'na' or 'name' @@ -61,6 +57,7 @@ public class SuggestToken { @Override public String toString() { - return "SuggestToken [start=" + start + ", end=" + end + ", suggestion=" + suggestion + "]"; + return "SuggestToken [start=" + start + ", end=" + end + ", suggestion=" + suggestion + ", tokenImageName=" + + tokenImageName + "]"; } } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/ParseExceptionWrapper.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/ParseExceptionWrapper.java index 08978eaaa..b2f5a6e42 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/ParseExceptionWrapper.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/ParseExceptionWrapper.java @@ -19,7 +19,8 @@ import cz.jirutka.rsql.parser.ParseException; * A {@link ParseException} wrapper which allows to access the parsing * information from the exception using reflection due there is no other access * of this information. See issue for requesting feature - * {@link https://github.com/jirutka/rsql-parser/issues/22} + * https://github.com + * /jirutka/rsql-parser/issues/22 */ public class ParseExceptionWrapper { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlParserValidationOracle.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlParserValidationOracle.java index aa31bb4da..641b9a291 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlParserValidationOracle.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlParserValidationOracle.java @@ -51,7 +51,9 @@ import cz.jirutka.rsql.parser.RSQLParserException; * {@link ParseException}. * * There is a feature request on the GitHub project - * {@link https://github.com/jirutka/rsql-parser/issues/22}. + * https://github.com + * /jirutka/rsql-parser/issues/22 + * */ public class RsqlParserValidationOracle implements RsqlValidationOracle { diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterHeader.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterHeader.java index 777185750..fd86f138f 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterHeader.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterHeader.java @@ -27,9 +27,9 @@ import org.eclipse.hawkbit.ui.filtermanagement.AutoCompleteTextFieldComponent.Fi import org.eclipse.hawkbit.ui.filtermanagement.event.CustomFilterUIEvent; import org.eclipse.hawkbit.ui.filtermanagement.state.FilterManagementUIState; import org.eclipse.hawkbit.ui.utils.I18N; -import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions; import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.UINotification; import org.springframework.beans.factory.annotation.Autowired; import org.vaadin.spring.events.EventBus; @@ -363,12 +363,6 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button return saveButton; } - /* - * (non-Javadoc) - * - * @see com.vaadin.ui.Button.ClickListener#buttonClick(com.vaadin.ui.Button. - * ClickEvent) - */ @Override public void buttonClick(final ClickEvent event) { if (UIComponentIdProvider.CUSTOM_FILTER_SAVE_ICON.equals(event.getComponent().getId()) diff --git a/hawkbit-ui/src/main/resources/VAADIN/themes/hawkbit/customstyles/target-filter-query.scss b/hawkbit-ui/src/main/resources/VAADIN/themes/hawkbit/customstyles/target-filter-query.scss index 102f6a3c9..2344b35cc 100644 --- a/hawkbit-ui/src/main/resources/VAADIN/themes/hawkbit/customstyles/target-filter-query.scss +++ b/hawkbit-ui/src/main/resources/VAADIN/themes/hawkbit/customstyles/target-filter-query.scss @@ -23,7 +23,7 @@ } .gwt-MenuBar-autocomplete .gwt-MenuItem-selected { - background-color: #197de1 !important; + background-color: $hawkbit-primary-color; background-image: linear-gradient(to bottom, #1b87e3 2%, #166ed5 98%) !important; color: #ecf2f8 !important; text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.05) !important; From 91383e9625bb50ad0f52361506e1e35e41b21fa1 Mon Sep 17 00:00:00 2001 From: kaizimmerm Date: Wed, 21 Sep 2016 18:08:59 +0200 Subject: [PATCH 17/43] Included some hints from review. Signed-off-by: kaizimmerm --- .../jpa/JpaControllerManagement.java | 2 ++ .../jpa/JpaRolloutGroupManagement.java | 4 +-- .../repository/jpa/JpaSoftwareManagement.java | 2 +- .../repository/jpa/model/JpaActionStatus.java | 6 +++-- .../ui/push/DelayedEventBusPushStrategy.java | 27 +++++++++---------- 5 files changed, 21 insertions(+), 20 deletions(-) diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java index c12838c9b..03ace2e1f 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java @@ -165,6 +165,8 @@ public class JpaControllerManagement implements ControllerManagement { @Override public Optional findOldestActiveActionByTarget(final Target target) { + // used in favorite to findFirstByTargetAndActiveOrderByIdAsc due to + // DATAJPA-841 issue. return actionRepository.findFirstByTargetAndActive(new Sort(Direction.ASC, "id"), (JpaTarget) target, true); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutGroupManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutGroupManagement.java index 64d81cb70..eba63b6c6 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutGroupManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutGroupManagement.java @@ -114,7 +114,7 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement { for (final RolloutGroup rolloutGroup : rolloutGroups) { final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus( - allStatesForRollout.get(rolloutGroup.getId()), new Long(rolloutGroup.getTotalTargets())); + allStatesForRollout.get(rolloutGroup.getId()), Long.valueOf(rolloutGroup.getTotalTargets())); rolloutGroup.setTotalTargetCountStatus(totalTargetCountStatus); } @@ -128,7 +128,7 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement { .getStatusCountByRolloutGroupId(rolloutGroupId); final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(rolloutStatusCountItems, - new Long(rolloutGroup.getTotalTargets())); + Long.valueOf(rolloutGroup.getTotalTargets())); rolloutGroup.setTotalTargetCountStatus(totalTargetCountStatus); return rolloutGroup; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareManagement.java index 62ebfcb69..401c20895 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareManagement.java @@ -204,7 +204,7 @@ public class JpaSoftwareManagement implements SoftwareManagement { @Override public Long countSoftwareModulesByType(final SoftwareModuleType type) { - final List> specList = new LinkedList<>(); + final List> specList = new ArrayList<>(2); Specification spec = SoftwareModuleSpecification.equalType((JpaSoftwareModuleType) type); specList.add(spec); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaActionStatus.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaActionStatus.java index 061351053..8fa774c00 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaActionStatus.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaActionStatus.java @@ -49,6 +49,8 @@ import com.google.common.base.Splitter; // sub entities @SuppressWarnings("squid:S2160") public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements ActionStatus { + private static final int MESSAGE_ENTRY_LENGTH = 512; + private static final long serialVersionUID = 1L; @Column(name = "target_occurred_at") @@ -138,9 +140,9 @@ public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements public final void addMessage(final String message) { if (message != null) { if (messages == null) { - messages = new ArrayList<>((message.length() / 512) + 1); + messages = new ArrayList<>((message.length() / MESSAGE_ENTRY_LENGTH) + 1); } - Splitter.fixedLength(512).split(message).forEach(messages::add); + Splitter.fixedLength(MESSAGE_ENTRY_LENGTH).split(message).forEach(messages::add); } } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/push/DelayedEventBusPushStrategy.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/push/DelayedEventBusPushStrategy.java index 0fdb06a46..5f72478cb 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/push/DelayedEventBusPushStrategy.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/push/DelayedEventBusPushStrategy.java @@ -20,6 +20,7 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import org.eclipse.hawkbit.eventbus.event.EntityEvent; +import org.eclipse.hawkbit.eventbus.event.Event; import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails; import org.eclipse.hawkbit.ui.UIEventProvider; import org.slf4j.Logger; @@ -98,7 +99,7 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { */ @Subscribe @AllowConcurrentEvents - public void dispatch(final org.eclipse.hawkbit.eventbus.event.Event event) { + public void dispatch(final Event event) { // to dispatch too many events which are not interested on the UI if (!isEventProvided(event)) { LOG.trace("Event is not supported in the UI!!! Dropped event is {}", event); @@ -111,7 +112,7 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { } } - private boolean isEventProvided(final org.eclipse.hawkbit.eventbus.event.Event event) { + private boolean isEventProvided(final Event event) { return eventProvider.getSingleEvents().contains(event.getClass()) || eventProvider.getBulkEvents().contains(event.getClass()); } @@ -148,8 +149,7 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { * @return {@code true} if the event can be dispatched to the UI otherwise * {@code false} */ - protected static boolean eventSecurityCheck(final SecurityContext userContext, - final org.eclipse.hawkbit.eventbus.event.Event event) { + protected static boolean eventSecurityCheck(final SecurityContext userContext, final Event event) { if (userContext == null || userContext.getAuthentication() == null) { return false; } @@ -182,13 +182,13 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { return; } - final List events = new ArrayList<>(size); + final List events = new ArrayList<>(size); for (int i = 0; i < size; i++) { - final org.eclipse.hawkbit.eventbus.event.Event pollEvent = queue.poll(); + final Event pollEvent = queue.poll(); if (pollEvent == null) { continue; } - events.add(i, pollEvent); + events.add(pollEvent); } if (events.isEmpty()) { @@ -213,8 +213,7 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { } - private void doDispatch(final List events, - final WrappedSession wrappedSession) { + private void doDispatch(final List events, final WrappedSession wrappedSession) { final SecurityContext userContext = (SecurityContext) wrappedSession .getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY); final SecurityContext oldContext = SecurityContextHolder.getContext(); @@ -225,10 +224,10 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { if (vaadinSession.getState() != State.OPEN) { return; } - LOG.debug("UI EventBus aggregator of UI {} got lock on session.", uiid); + LOG.debug("UI EventBus aggregator of UI {} got lock on session.", vaadinUI.getUIId()); fowardSingleEvents(events, userContext); fowardBulkEvents(events, userContext); - LOG.debug("UI EventBus aggregator of UI {} left lock on session.", uiid); + LOG.debug("UI EventBus aggregator of UI {} left lock on session.", vaadinUI.getUIId()); }).get(); } catch (InterruptedException | ExecutionException e) { LOG.error("Wait for Vaadin session for UI {} interrupted!", uiid, e); @@ -237,8 +236,7 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { } } - private void fowardBulkEvents(final List events, - final SecurityContext userContext) { + private void fowardBulkEvents(final List events, final SecurityContext userContext) { final Set> filterBulkEvenTypes = eventProvider.getFilteredBulkEventsType(events); for (final Class bulkType : filterBulkEvenTypes) { @@ -252,8 +250,7 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { } } - private void fowardSingleEvents(final List events, - final SecurityContext userContext) { + private void fowardSingleEvents(final List events, final SecurityContext userContext) { events.stream() .filter(event -> DelayedEventBusPushStrategy.eventSecurityCheck(userContext, event) && eventProvider.getSingleEvents().contains(event.getClass())) From 948196ae28c5228aef4919c77456a81046b04af3 Mon Sep 17 00:00:00 2001 From: kaizimmerm Date: Thu, 22 Sep 2016 11:48:41 +0200 Subject: [PATCH 18/43] Added immutable comments. Signed-off-by: kaizimmerm --- .../repository/model/ActionStatus.java | 2 +- .../repository/model/DistributionSetTag.java | 4 ++-- .../repository/model/DistributionSetType.java | 6 +++--- .../hawkbit/repository/model/Rollout.java | 2 +- .../repository/model/SoftwareModule.java | 11 ++++++++--- .../hawkbit/repository/model/Target.java | 19 +++++++++++++++++-- .../hawkbit/repository/model/TargetTag.java | 2 +- .../repository/jpa/JpaTargetManagement.java | 3 ++- 8 files changed, 35 insertions(+), 14 deletions(-) diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/ActionStatus.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/ActionStatus.java index d38bc4f75..2b5daed2e 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/ActionStatus.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/ActionStatus.java @@ -50,7 +50,7 @@ public interface ActionStatus extends TenantAwareBaseEntity { short getDownloadProgressPercent(); /** - * @return list of message entries that can be added to the + * @return immutable list of message entries that in the * {@link ActionStatus}. */ List getMessages(); diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetTag.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetTag.java index 05089d138..9507e29b2 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetTag.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetTag.java @@ -17,8 +17,8 @@ import java.util.List; public interface DistributionSetTag extends Tag { /** - * @return {@link List} of {@link DistributionSet}s this {@link Tag} is - * assigned to. + * @return immutable {@link List} of {@link DistributionSet}s this + * {@link Tag} is assigned to. */ List getAssignedToDistributionSet(); diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetType.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetType.java index cf5da40b2..57420bbad 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetType.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetType.java @@ -27,15 +27,15 @@ public interface DistributionSetType extends NamedEntity { boolean isDeleted(); /** - * @return set of {@link SoftwareModuleType}s that need to be in a + * @return immutable set of {@link SoftwareModuleType}s that need to be in a * {@link DistributionSet} of this type to be * {@link DistributionSet#isComplete()}. */ Set getMandatoryModuleTypes(); /** - * @return set of optional {@link SoftwareModuleType}s that can be in a - * {@link DistributionSet} of this type. + * @return immutable set of optional {@link SoftwareModuleType}s that can be + * in a {@link DistributionSet} of this type. */ Set getOptionalModuleTypes(); diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Rollout.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Rollout.java index 37185a8e2..72d7e7c53 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Rollout.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Rollout.java @@ -38,7 +38,7 @@ public interface Rollout extends NamedEntity { void setDistributionSet(DistributionSet distributionSet); /** - * @return list of deployment groups of the rollout. + * @return immutable list of deployment groups of the rollout. */ List getRolloutGroups(); diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModule.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModule.java index 3c3a24fd0..e3bf1468f 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModule.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModule.java @@ -11,6 +11,10 @@ package org.eclipse.hawkbit.repository.model; import java.util.List; import java.util.Optional; +/** + * Software package as sub element of a {@link DistributionSet}. + * + */ public interface SoftwareModule extends NamedVersionedEntity { /** @@ -40,12 +44,12 @@ public interface SoftwareModule extends NamedVersionedEntity { Optional getLocalArtifactByFilename(String fileName); /** - * @return the artifacts + * @return immutable list of all artifacts */ List getArtifacts(); /** - * @return local artifacts only + * @return immutable list of local artifacts only */ List getLocalArtifacts(); @@ -104,7 +108,8 @@ public interface SoftwareModule extends NamedVersionedEntity { List getMetadata(); /** - * @return the assignedTo + * @return immutable list of {@link DistributionSet}s the module is assigned + * to */ List getAssignedTo(); diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Target.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Target.java index c09b28a2f..c07a8c224 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Target.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Target.java @@ -32,12 +32,12 @@ public interface Target extends NamedEntity { String getControllerId(); /** - * @return assigned {@link TargetTag}s. + * @return immutable set of assigned {@link TargetTag}s. */ Set getTags(); /** - * @return {@link Action} history of the {@link Target}. + * @return immutable {@link Action} history of the {@link Target}. */ List getActions(); @@ -64,4 +64,19 @@ public interface Target extends NamedEntity { */ void setSecurityToken(String token); + /** + * @param tag + * to add + * @return true if tag could be added sucessfully (i.e. was not + * already in the list). + */ + public boolean addTag(TargetTag tag); + + /** + * @param tag + * to remove + * @return true if tag was in the list and removed + */ + public boolean removeTag(TargetTag tag); + } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetTag.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetTag.java index f222e9e90..afe0ff59a 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetTag.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetTag.java @@ -17,7 +17,7 @@ import java.util.List; public interface TargetTag extends Tag { /** - * @return {@link List} of targets assigned to this {@link Tag}. + * @return immutable {@link List} of targets assigned to this {@link Tag}. */ List getAssignedToTargets(); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java index 67c28216d..b9085c601 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java @@ -343,7 +343,7 @@ public class JpaTargetManagement implements TargetManagement { // all are already assigned -> unassign if (alreadyAssignedTargets.size() == allTargets.size()) { - alreadyAssignedTargets.forEach(target -> ((JpaTarget) target).removeTag(tag)); + alreadyAssignedTargets.forEach(target -> target.removeTag(tag)); final TargetTagAssignmentResult result = new TargetTagAssignmentResult(0, 0, alreadyAssignedTargets.size(), Collections.emptyList(), alreadyAssignedTargets, tag); @@ -385,6 +385,7 @@ public class JpaTargetManagement implements TargetManagement { } private List unAssignTag(final Collection targets, final TargetTag tag) { + @SuppressWarnings({ "unchecked", "rawtypes" }) final Collection toUnassign = (Collection) targets; toUnassign.forEach(target -> target.removeTag(tag)); From 5281b9b2da01afb92faf8cec340c63ca8f95c0eb Mon Sep 17 00:00:00 2001 From: Michael Hirsch Date: Mon, 26 Sep 2016 08:56:16 +0200 Subject: [PATCH 19/43] add sonar quality badge Signed-off-by: Michael Hirsch --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bc00c5c8d..26e2b6284 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,12 @@ -Build: [![Circle CI](https://circleci.com/gh/eclipse/hawkbit.svg?style=svg)](https://circleci.com/gh/eclipse/hawkbit) - # Eclipse.IoT hawkBit - Update Server [hawkBit](https://projects.eclipse.org/projects/iot.hawkbit) is an domain independent back end solution for rolling out software updates to constrained edge devices as well as more powerful controllers and gateways connected to IP based networking infrastructure. +Build: [![Circle CI](https://circleci.com/gh/eclipse/hawkbit.svg?style=svg)](https://circleci.com/gh/eclipse/hawkbit) +[![SonarQuality](https://sonar.eu-gb.mybluemix.net/api/badges/gate?key=org.eclipse.hawkbit:hawkbit-parent)](https://sonar.eu-gb.mybluemix.net) + # Documentation see [hawkBit Wiki](https://github.com/eclipse/hawkbit/wiki) From 8515652cd5d9759e78bddf37f03eda8aced741f8 Mon Sep 17 00:00:00 2001 From: Michael Hirsch Date: Mon, 26 Sep 2016 09:20:23 +0200 Subject: [PATCH 20/43] use circleCI badge style shield Signed-off-by: Michael Hirsch --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 26e2b6284..778391f32 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [hawkBit](https://projects.eclipse.org/projects/iot.hawkbit) is an domain independent back end solution for rolling out software updates to constrained edge devices as well as more powerful controllers and gateways connected to IP based networking infrastructure. -Build: [![Circle CI](https://circleci.com/gh/eclipse/hawkbit.svg?style=svg)](https://circleci.com/gh/eclipse/hawkbit) +Build: [![Circle CI](https://circleci.com/gh/eclipse/hawkbit.svg?style=shield)](https://circleci.com/gh/eclipse/hawkbit) [![SonarQuality](https://sonar.eu-gb.mybluemix.net/api/badges/gate?key=org.eclipse.hawkbit:hawkbit-parent)](https://sonar.eu-gb.mybluemix.net) # Documentation From c44f1d5e4461eefdf68bd73028196e949166b13a Mon Sep 17 00:00:00 2001 From: kaizimmerm Date: Mon, 26 Sep 2016 13:18:43 +0200 Subject: [PATCH 21/43] Removed JPA dependency Signed-off-by: kaizimmerm --- .../MgmtDistributionSetTypeResourceTest.java | 9 ++------- .../resource/MgmtSoftwareModuleResourceTest.java | 13 +++++-------- .../MgmtSoftwareModuleTypeResourceTest.java | 8 +++----- 3 files changed, 10 insertions(+), 20 deletions(-) diff --git a/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeResourceTest.java b/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeResourceTest.java index f3b19f5c8..0dd90b35f 100644 --- a/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeResourceTest.java +++ b/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeResourceTest.java @@ -26,7 +26,6 @@ import java.util.List; import org.apache.commons.lang3.RandomStringUtils; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; -import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType; import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; @@ -495,9 +494,6 @@ public class MgmtDistributionSetTypeResourceTest extends AbstractRestIntegration final SoftwareModuleType testSmType = softwareManagement.createSoftwareModuleType( entityFactory.generateSoftwareModuleType("test123", "TestName123", "Desc123", 5)); - final List types = new ArrayList<>(); - types.add(testType); - // DST does not exist mvc.perform(get("/rest/v1/distributionsettypes/12345678")).andDo(MockMvcResultPrinter.print()) .andExpect(status().isNotFound()); @@ -555,9 +551,8 @@ public class MgmtDistributionSetTypeResourceTest extends AbstractRestIntegration .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) .andExpect(status().isBadRequest()); - final DistributionSetType missingName = new JpaDistributionSetType("test123", null, "Desc123"); - mvc.perform(post("/rest/v1/distributionsettypes") - .content(JsonBuilder.distributionSetTypes(Lists.newArrayList(missingName))) + // Missing mandatory field name + mvc.perform(post("/rest/v1/distributionsettypes").content("[{\"description\":\"Desc123\",\"key\":\"test123\"}]") .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) .andExpect(status().isBadRequest()); diff --git a/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResourceTest.java b/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResourceTest.java index f3f86fbc7..4f3e12b16 100644 --- a/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResourceTest.java +++ b/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResourceTest.java @@ -37,7 +37,6 @@ import org.eclipse.hawkbit.exception.SpServerError; import org.eclipse.hawkbit.mgmt.json.model.artifact.MgmtArtifact; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; -import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule; import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.LocalArtifact; @@ -439,8 +438,7 @@ public class MgmtSoftwareModuleResourceTest extends AbstractRestIntegrationTestW SoftwareModule sm = entityFactory.generateSoftwareModule(osType, "name 1", "version 1", null, null); sm = softwareManagement.createSoftwareModule(sm); - final List modules = new ArrayList<>(); - modules.add(sm); + final List modules = Lists.newArrayList(sm); // SM does not exist mvc.perform(get("/rest/v1/softwaremodules/12345678")).andDo(MockMvcResultPrinter.print()) @@ -458,11 +456,10 @@ public class MgmtSoftwareModuleResourceTest extends AbstractRestIntegrationTestW .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) .andExpect(status().isBadRequest()); - final SoftwareModule missingName = new JpaSoftwareModule(osType, null, "version 1", null, null); - mvc.perform( - post("/rest/v1/softwaremodules").content(JsonBuilder.softwareModules(Lists.newArrayList(missingName))) - .contentType(MediaType.APPLICATION_JSON)) - .andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest()); + mvc.perform(post("/rest/v1/softwaremodules") + .content("[{\"description\":\"Desc123\",\"key\":\"test123\", \"type\":\"os\"}]") + .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) + .andExpect(status().isBadRequest()); final SoftwareModule toLongName = entityFactory.generateSoftwareModule(osType, RandomStringUtils.randomAscii(80), "version 1", null, null); diff --git a/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeResourceTest.java b/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeResourceTest.java index a0a5b397b..8150eba54 100644 --- a/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeResourceTest.java +++ b/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeResourceTest.java @@ -26,7 +26,6 @@ import java.util.List; import org.apache.commons.lang3.RandomStringUtils; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; -import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType; import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.test.util.WithUser; @@ -319,8 +318,7 @@ public class MgmtSoftwareModuleTypeResourceTest extends AbstractRestIntegrationT final SoftwareModuleType testType = softwareManagement.createSoftwareModuleType( entityFactory.generateSoftwareModuleType("test123", "TestName123", "Desc123", 5)); - final List types = new ArrayList<>(); - types.add(testType); + final List types = Lists.newArrayList(testType); // SM does not exist mvc.perform(get("/rest/v1/softwaremoduletypes/12345678")).andDo(MockMvcResultPrinter.print()) @@ -338,9 +336,9 @@ public class MgmtSoftwareModuleTypeResourceTest extends AbstractRestIntegrationT .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) .andExpect(status().isBadRequest()); - final SoftwareModuleType missingName = new JpaSoftwareModuleType("test123", null, "Desc123", 5); mvc.perform(post("/rest/v1/softwaremoduletypes") - .content(JsonBuilder.softwareModuleTypes(Lists.newArrayList(missingName))) + .content( + "[{\"description\":\"Desc123\",\"id\":9223372036854775807,\"key\":\"test123\",\"maxAssignments\":5}]") .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) .andExpect(status().isBadRequest()); From ae9f2b178d0547e4260b9576b7d527724ab998bf Mon Sep 17 00:00:00 2001 From: kaizimmerm Date: Mon, 26 Sep 2016 15:10:23 +0200 Subject: [PATCH 22/43] Add file exclusions to script Signed-off-by: kaizimmerm --- sonarCircleCi.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonarCircleCi.sh b/sonarCircleCi.sh index cf03e5593..9a00961c8 100644 --- a/sonarCircleCi.sh +++ b/sonarCircleCi.sh @@ -10,7 +10,7 @@ echo $CI_PULL_REQUEST pull request # regular sonar on master if [ "$CIRCLE_BRANCH" = "master" ]; then - mvn verify license:check sonar:sonar -Dsonar.login=$SONAR_SERVER_USER -Dsonar.password=$SONAR_SERVER_PASSWD + mvn verify license:check sonar:sonar -Dsonar.login=$SONAR_SERVER_USER -Dsonar.password=$SONAR_SERVER_PASSWD -Dsonar.exclusions=**/target/generated-sources/apt/**,**/src/test/**,**/src/main/java/org/eclipse/hawkbit/repository/test/** -Dsonar.coverage.exclusions=**/src/main/java/org/eclipse/hawkbit/ui/**,**/target/generated-sources/apt/**,**/src/main/java/org/eclipse/hawkbit/repository/test/** # preview in case of pull request - disabled as circle does not fill those with pull reuqests from different directories else #if [ -n "$CI_PULL_REQUEST" ]; then From 51c52b87b7307e8c674fcbc05d449144eccc77e1 Mon Sep 17 00:00:00 2001 From: kaizimmerm Date: Mon, 26 Sep 2016 15:53:24 +0200 Subject: [PATCH 23/43] Fixed sonar issues Signed-off-by: kaizimmerm --- .../hawkbit/ui/menu/DashboardMenu.java | 2 +- .../rollout/AddUpdateRolloutWindowLayout.java | 59 +++++-------------- 2 files changed, 15 insertions(+), 46 deletions(-) diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/menu/DashboardMenu.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/menu/DashboardMenu.java index fb3dc6c9f..c35378e20 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/menu/DashboardMenu.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/menu/DashboardMenu.java @@ -296,7 +296,7 @@ public final class DashboardMenu extends CustomComponent { final Optional findFirst = dashboardVaadinViews.stream() .filter(view -> view.getViewName().equals(viewName)).findFirst(); - if (findFirst == null || !findFirst.isPresent()) { + if (!findFirst.isPresent()) { return null; } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rollout/AddUpdateRolloutWindowLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rollout/AddUpdateRolloutWindowLayout.java index fd551a9b1..dae322b2e 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rollout/AddUpdateRolloutWindowLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rollout/AddUpdateRolloutWindowLayout.java @@ -43,10 +43,10 @@ import org.eclipse.hawkbit.ui.rollout.event.RolloutEvent; import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil; import org.eclipse.hawkbit.ui.utils.I18N; import org.eclipse.hawkbit.ui.utils.SPDateTimeUtil; -import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions; import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.UINotification; import org.springframework.beans.factory.annotation.Autowired; import org.vaadin.addons.lazyquerycontainer.BeanQueryFactory; @@ -610,26 +610,15 @@ public class AddUpdateRolloutWindowLayout extends GridLayout { @Override public void validate(final Object value) { - try { - if (isNoOfGroupsOrTargetFilterEmpty()) { - uiNotification - .displayValidationError(i18n.get("message.rollout.noofgroups.or.targetfilter.missing")); - } else { - if (value != null) { - final int groupSize = getGroupSize(); - new IntegerRangeValidator(i18n.get(MESSAGE_ROLLOUT_FIELD_VALUE_RANGE, 0, groupSize), 0, - groupSize).validate(Integer.valueOf(value.toString())); - } + if (isNoOfGroupsOrTargetFilterEmpty()) { + uiNotification.displayValidationError(i18n.get("message.rollout.noofgroups.or.targetfilter.missing")); + } else { + if (value != null) { + final int groupSize = getGroupSize(); + new IntegerRangeValidator(i18n.get(MESSAGE_ROLLOUT_FIELD_VALUE_RANGE, 0, groupSize), 0, groupSize) + .validate(Integer.valueOf(value.toString())); } } - // suppress the need of preserve original exception, will blow - // up the - // log and not necessary here - catch (final InvalidValueException ex) { - // we have to throw the exception here, otherwise the UI won't - // show the vaadin validation error! - throw ex; - } } private boolean isNoOfGroupsOrTargetFilterEmpty() { @@ -648,19 +637,9 @@ public class AddUpdateRolloutWindowLayout extends GridLayout { @Override public void validate(final Object value) { - try { - if (value != null) { - new IntegerRangeValidator(i18n.get(MESSAGE_ROLLOUT_FIELD_VALUE_RANGE, 0, 100), 0, 100) - .validate(Integer.valueOf(value.toString())); - } - } - // suppress the need of preserve original exception, will blow - // up the - // log and not necessary here - catch (final InvalidValueException ex) { - // we have to throw the exception here, otherwise the UI won't - // show the vaadin validation error! - throw ex; + if (value != null) { + new IntegerRangeValidator(i18n.get(MESSAGE_ROLLOUT_FIELD_VALUE_RANGE, 0, 100), 0, 100) + .validate(Integer.valueOf(value.toString())); } } } @@ -670,19 +649,9 @@ public class AddUpdateRolloutWindowLayout extends GridLayout { @Override public void validate(final Object value) { - try { - if (value != null) { - new IntegerRangeValidator(i18n.get(MESSAGE_ROLLOUT_FIELD_VALUE_RANGE, 0, 500), 0, 500) - .validate(Integer.valueOf(value.toString())); - } - } - // suppress the need of preserve original exception, will blow - // up the - // log and not necessary here - catch (final InvalidValueException ex) { - // we have to throw the exception here, otherwise the UI won't - // show the vaadin validation error! - throw ex; + if (value != null) { + new IntegerRangeValidator(i18n.get(MESSAGE_ROLLOUT_FIELD_VALUE_RANGE, 0, 500), 0, 500) + .validate(Integer.valueOf(value.toString())); } } } From 760484921bffa650969d730b5bce3555890515fc Mon Sep 17 00:00:00 2001 From: kaizimmerm Date: Mon, 26 Sep 2016 15:55:51 +0200 Subject: [PATCH 24/43] Add missing override Signed-off-by: kaizimmerm --- .../org/eclipse/hawkbit/repository/jpa/model/JpaTarget.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTarget.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTarget.java index 4b0041a00..31bb5a97e 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTarget.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTarget.java @@ -173,6 +173,7 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable(); @@ -181,6 +182,7 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable Date: Mon, 26 Sep 2016 16:38:51 +0200 Subject: [PATCH 25/43] Added the search icon for executing the filter query. Signed-off-by: Gaurav --- .../CreateOrUpdateFilterHeader.java | 24 ++++++++++++++++++- .../ui/utils/UIComponentIdProvider.java | 4 ++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterHeader.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterHeader.java index fd86f138f..b9e98c9f0 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterHeader.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterHeader.java @@ -52,6 +52,7 @@ import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.Label; import com.vaadin.ui.Link; import com.vaadin.ui.TextField; +import com.vaadin.ui.UI; import com.vaadin.ui.VerticalLayout; import com.vaadin.ui.themes.ValoTheme; @@ -111,6 +112,8 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button private Link helpLink; + private Button searchIcon; + private String oldFilterName; private String oldFilterQuery; @@ -195,6 +198,7 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button nameTextField.setWidth(380, Unit.PIXELS); saveButton = createSaveButton(); + searchIcon = createSearchIcon(); helpLink = SPUIComponentProvider.getHelpLink(uiProperties.getLinks().getDocumentation().getTargetfilterView()); @@ -292,7 +296,7 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button final HorizontalLayout iconLayout = new HorizontalLayout(); iconLayout.setSizeUndefined(); iconLayout.setSpacing(false); - iconLayout.addComponents(helpLink, saveButton); + iconLayout.addComponents(helpLink, searchIcon, saveButton); final HorizontalLayout queryLayout = new HorizontalLayout(); queryLayout.setSizeUndefined(); @@ -324,10 +328,12 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button if (validationFailed || (isNameAndQueryEmpty(nameTextField.getValue(), query) || (query.equals(oldFilterQuery) && nameTextField.getValue().equals(oldFilterName)))) { saveButton.setEnabled(false); + searchIcon.setEnabled(false); } else { if (hasSavePermission()) { saveButton.setEnabled(true); } + searchIcon.setEnabled(true); } } @@ -363,6 +369,22 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button return saveButton; } + private Button createSearchIcon() { + searchIcon = SPUIComponentProvider.getButton(UIComponentIdProvider.FILTER_SEARCH_ICON_ID, "", "", null, false, + FontAwesome.SEARCH, SPUIButtonStyleSmallNoBorder.class); + searchIcon.addClickListener(event -> onSearchIconClick()); + searchIcon.setEnabled(false); + searchIcon.setData(false); + return searchIcon; + } + + private void onSearchIconClick() { + if (!queryTextField.isValidationError()) { + queryTextField.showValidationInProgress(); + queryTextField.getExecutor().execute(queryTextField.new StatusCircledAsync(UI.getCurrent())); + } + } + @Override public void buttonClick(final ClickEvent event) { if (UIComponentIdProvider.CUSTOM_FILTER_SAVE_ICON.equals(event.getComponent().getId()) diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIComponentIdProvider.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIComponentIdProvider.java index e3475d20d..09540f83e 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIComponentIdProvider.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIComponentIdProvider.java @@ -876,6 +876,10 @@ public final class UIComponentIdProvider { * ID for download anonymous checkbox */ public static final String DOWNLOAD_ANONYMOUS_CHECKBOX = "downloadanonymouscheckbox"; + /** + * Id of custom filter query search Icon. + */ + public static final String FILTER_SEARCH_ICON_ID = "filter.search.icon"; /** * /* Private Constructor. From 97b3313642aeaa79f5871d0124ff5c473b129b84 Mon Sep 17 00:00:00 2001 From: kaizimmerm Date: Tue, 27 Sep 2016 09:43:46 +0200 Subject: [PATCH 26/43] Fixed Sonar issues Signed-off-by: kaizimmerm --- .../rest/api/DdiRootControllerRestApi.java | 20 ++++++++++++++++++- .../resource/DdiArtifactStoreController.java | 2 +- .../ddi/rest/resource/DdiRootController.java | 8 ++++++++ .../amqp/AmqpDeadletterProperties.java | 3 ++- .../eclipse/hawkbit/amqp/AmqpProperties.java | 20 ++++++++++++++----- .../resource/MgmtSoftwareModuleResource.java | 5 +++++ .../src/test/resources/logback-spring.xml | 2 +- .../upload/UploadConfirmationWindow.java | 6 +++++- .../ui/artifacts/upload/UploadLayout.java | 7 ++++++- .../dstable/DistributionSetDetails.java | 8 +++++--- .../CreateOrUpdateFilterHeader.java | 8 ++++++-- .../management/dstable/DistributionTable.java | 6 +++++- 12 files changed, 78 insertions(+), 17 deletions(-) diff --git a/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/rest/api/DdiRootControllerRestApi.java b/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/rest/api/DdiRootControllerRestApi.java index f67e0591b..c33a07d60 100644 --- a/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/rest/api/DdiRootControllerRestApi.java +++ b/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/rest/api/DdiRootControllerRestApi.java @@ -37,7 +37,9 @@ public interface DdiRootControllerRestApi { /** * Returns all artifacts of a given software module and target. - * + * + * @param tenant + * of the request * @param targetid * of the target that matches to controller id * @param softwareModuleId @@ -53,6 +55,8 @@ public interface DdiRootControllerRestApi { /** * Root resource for an individual {@link Target}. * + * @param tenant + * of the request * @param targetid * of the target that matches to controller id * @param request @@ -68,6 +72,8 @@ public interface DdiRootControllerRestApi { * Handles GET {@link DdiArtifact} download request. This could be full or * partial (as specified by RFC7233 (Range Requests)) download request. * + * @param tenant + * of the request * @param targetid * of the related target * @param softwareModuleId @@ -92,6 +98,8 @@ public interface DdiRootControllerRestApi { /** * Handles GET {@link DdiArtifact} MD5 checksum file download request. * + * @param tenant + * of the request * @param targetid * of the related target * @param softwareModuleId @@ -116,6 +124,8 @@ public interface DdiRootControllerRestApi { /** * Resource for software module. * + * @param tenant + * of the request * @param targetid * of the target that matches to controller id * @param actionId @@ -139,6 +149,8 @@ public interface DdiRootControllerRestApi { /** * This is the feedback channel for the {@link DdiDeploymentBase} action. * + * @param tenant + * of the request * @param feedback * to provide * @param targetid @@ -159,6 +171,8 @@ public interface DdiRootControllerRestApi { /** * This is the feedback channel for the config data action. * + * @param tenant + * of the request * @param configData * as body * @param targetid @@ -176,6 +190,8 @@ public interface DdiRootControllerRestApi { /** * RequestMethod.GET method for the {@link DdiCancel} action. * + * @param tenant + * of the request * @param targetid * ID of the calling target * @param actionId @@ -195,6 +211,8 @@ public interface DdiRootControllerRestApi { * RequestMethod.POST method receiving the {@link DdiActionFeedback} from * the target. * + * @param tenant + * of the request * @param feedback * the {@link DdiActionFeedback} from the target. * @param targetid diff --git a/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiArtifactStoreController.java b/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiArtifactStoreController.java index 3feb3068f..d3f57a7f7 100644 --- a/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiArtifactStoreController.java +++ b/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiArtifactStoreController.java @@ -35,7 +35,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.security.web.bind.annotation.AuthenticationPrincipal; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.context.WebApplicationContext; diff --git a/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootController.java b/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootController.java index 33d7daf69..5e5c0d7c5 100644 --- a/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootController.java +++ b/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootController.java @@ -151,7 +151,12 @@ public class DdiRootController implements DdiRootControllerRestApi { LOG.warn("Softare module with id {} could not be found.", softwareModuleId); result = new ResponseEntity<>(HttpStatus.NOT_FOUND); } else { + + // Exception squid:S3655 - Optional access is checked in checkModule + // subroutine + @SuppressWarnings("squid:S3655") final LocalArtifact artifact = module.getLocalArtifactByFilename(fileName).get(); + final DbArtifact file = artifactManagement.loadLocalArtifactBinary(artifact); final String ifMatch = requestResponseContextHolder.getHttpServletRequest().getHeader("If-Match"); @@ -196,6 +201,9 @@ public class DdiRootController implements DdiRootControllerRestApi { } @Override + // Exception squid:S3655 - Optional access is checked in checkModule + // subroutine + @SuppressWarnings("squid:S3655") public ResponseEntity downloadArtifactMd5(@PathVariable("tenant") final String tenant, @PathVariable("targetid") final String targetid, @PathVariable("softwareModuleId") final Long softwareModuleId, diff --git a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpDeadletterProperties.java b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpDeadletterProperties.java index 723caf44c..5a7e3e083 100644 --- a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpDeadletterProperties.java +++ b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpDeadletterProperties.java @@ -22,12 +22,13 @@ import com.google.common.collect.Maps; */ @ConfigurationProperties("hawkbit.dmf.rabbitmq.deadLetter") public class AmqpDeadletterProperties { + private static final int THREE_WEEKS = 21; /** * Message time to live (ttl) for the deadletter queue. Default ttl is 3 * weeks. */ - private long ttl = Duration.ofDays(21).toMillis(); + private long ttl = Duration.ofDays(THREE_WEEKS).toMillis(); /** * Return the deadletter arguments. diff --git a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpProperties.java b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpProperties.java index b90f8ca46..dd80262a5 100644 --- a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpProperties.java +++ b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpProperties.java @@ -20,6 +20,16 @@ import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties("hawkbit.dmf.rabbitmq") public class AmqpProperties { + private static final int ONE_MINUTE = 60; + + private static final int DEFAULT_QUEUE_DECLARATION_RETRIES = 50; + + private static final int DEFAULT_INITIAL_CONSUMERS = 3; + + private static final int DEFAULT_PREFETCH_COUNT = 10; + + private static final int DEFAULT_MAX_CONSUMERS = 10; + /** * Enable DMF API based on AMQP 0.9 */ @@ -54,24 +64,24 @@ public class AmqpProperties { /** * Requested heartbeat interval from broker in {@link TimeUnit#SECONDS}. */ - private int requestedHeartBeat = (int) TimeUnit.SECONDS.toSeconds(60); + private int requestedHeartBeat = (int) TimeUnit.SECONDS.toSeconds(ONE_MINUTE); /** * Sets an upper limit to the number of consumers. */ - private int maxConcurrentConsumers = 10; + private int maxConcurrentConsumers = DEFAULT_MAX_CONSUMERS; /** * Tells the broker how many messages to send to each consumer in a single * request. Often this can be set quite high to improve throughput. */ - private int prefetchCount = 10; + private int prefetchCount = DEFAULT_PREFETCH_COUNT; /** * Initial number of consumers. Is scaled up if necessary up to * {@link #maxConcurrentConsumers}. */ - private int initialConcurrentConsumers = 3; + private int initialConcurrentConsumers = DEFAULT_INITIAL_CONSUMERS; /** * The number of retry attempts when passive queue declaration fails. @@ -79,7 +89,7 @@ public class AmqpProperties { * consuming from multiple queues, when not all queues were available during * initialization. */ - private int declarationRetries = 50; + private int declarationRetries = DEFAULT_QUEUE_DECLARATION_RETRIES; public int getDeclarationRetries() { return declarationRetries; diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResource.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResource.java index ae099dcf6..1706a8030 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResource.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResource.java @@ -107,10 +107,15 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi { @Override @ResponseBody + // Exception squid:S3655 - Optional access is checked in + // findSoftwareModuleWithExceptionIfNotFound + // subroutine + @SuppressWarnings("squid:S3655") public ResponseEntity getArtifact(@PathVariable("softwareModuleId") final Long softwareModuleId, @PathVariable("artifactId") final Long artifactId) { final SoftwareModule module = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, artifactId); + return ResponseEntity.ok(toResponse(module.getLocalArtifact(artifactId).get())); } diff --git a/hawkbit-mgmt-resource/src/test/resources/logback-spring.xml b/hawkbit-mgmt-resource/src/test/resources/logback-spring.xml index 6dd6c94e0..39f7e6bbf 100644 --- a/hawkbit-mgmt-resource/src/test/resources/logback-spring.xml +++ b/hawkbit-mgmt-resource/src/test/resources/logback-spring.xml @@ -23,7 +23,7 @@ - + diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadConfirmationWindow.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadConfirmationWindow.java index 098ebb2c4..c71a58367 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadConfirmationWindow.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadConfirmationWindow.java @@ -32,11 +32,11 @@ import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleSmallNoBorder; import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleTiny; import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil; import org.eclipse.hawkbit.ui.utils.I18N; -import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions; import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions; import org.eclipse.hawkbit.ui.utils.SpringContextHelper; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -572,6 +572,9 @@ public class UploadConfirmationWindow implements Button.ClickListener { } + // Exception squid:S3655 - Optional access is checked in + // checkIfArtifactDetailsDispalyed subroutine + @SuppressWarnings("squid:S3655") private void processArtifactUpload() { final List itemIds = (List) uploadDetailsTable.getItemIds(); if (preUploadValidation(itemIds)) { @@ -593,6 +596,7 @@ public class UploadConfirmationWindow implements Button.ClickListener { } refreshArtifactDetailsLayout = checkIfArtifactDetailsDispalyed(bSoftwareModule.getId()); } + if (refreshArtifactDetailsLayout) { uploadLayout.refreshArtifactDetailsLayout(artifactUploadState.getSelectedBaseSoftwareModule().get()); } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadLayout.java index e887cf020..92f6a1f36 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadLayout.java @@ -34,10 +34,10 @@ import org.eclipse.hawkbit.ui.components.SPUIComponentProvider; import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleSmall; import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil; import org.eclipse.hawkbit.ui.utils.I18N; -import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions; import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.UINotification; import org.eclipse.hawkbit.util.SPInfo; import org.slf4j.Logger; @@ -254,6 +254,11 @@ public class UploadLayout extends VerticalLayout { final Html5File[] files = ((WrapperTransferable) event.getTransferable()).getFiles(); // selected software module at the time of file drop is // considered for upload + + if (!artifactUploadState.getSelectedBaseSoftwareModule().isPresent()) { + return; + } + final SoftwareModule selectedSw = artifactUploadState.getSelectedBaseSoftwareModule().get(); // reset the flag hasDirectory = Boolean.FALSE; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetDetails.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetDetails.java index 0b1fb5af2..b6f90e7ac 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetDetails.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetDetails.java @@ -173,9 +173,11 @@ public class DistributionSetDetails extends AbstractNamedVersionedEntityTableDet } private Button assignSoftModuleButton(final String softwareModuleName) { - if (getPermissionChecker().hasUpdateDistributionPermission() && distributionSetManagement - .findDistributionSetById(manageDistUIState.getLastSelectedDistribution().get().getId()) - .getAssignedTargets().isEmpty()) { + if (getPermissionChecker().hasUpdateDistributionPermission() + && manageDistUIState.getLastSelectedDistribution().isPresent() + && distributionSetManagement + .findDistributionSetById(manageDistUIState.getLastSelectedDistribution().get().getId()) + .getAssignedTargets().isEmpty()) { final Button reassignSoftModule = SPUIComponentProvider.getButton(softwareModuleName, "", "", "", true, FontAwesome.TIMES, SPUIButtonStyleSmallNoBorder.class); reassignSoftModule.setEnabled(false); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterHeader.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterHeader.java index 14e46cdf1..46b43dc5b 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterHeader.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterHeader.java @@ -26,9 +26,9 @@ import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleSmallNoBorder; import org.eclipse.hawkbit.ui.filtermanagement.event.CustomFilterUIEvent; import org.eclipse.hawkbit.ui.filtermanagement.state.FilterManagementUIState; import org.eclipse.hawkbit.ui.utils.I18N; -import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions; import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.UINotification; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -136,7 +136,7 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button private LayoutClickListener nameLayoutClickListner; - private boolean validationFailed = false; + private boolean validationFailed; /** * Initialize the Campaign Status History Header. @@ -509,6 +509,10 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button } private void updateCustomFilter() { + if (!filterManagementUIState.getTfQuery().isPresent()) { + return; + } + final TargetFilterQuery targetFilterQuery = filterManagementUIState.getTfQuery().get(); targetFilterQuery.setName(nameTextField.getValue()); targetFilterQuery.setQuery(queryTextField.getValue()); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTable.java index 749ed772c..728e1036b 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTable.java @@ -42,11 +42,11 @@ import org.eclipse.hawkbit.ui.management.event.PinUnpinEvent; import org.eclipse.hawkbit.ui.management.event.SaveActionWindowEvent; import org.eclipse.hawkbit.ui.management.state.ManagementUIState; import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil; -import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions; import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions; import org.eclipse.hawkbit.ui.utils.TableColumn; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.UINotification; import org.springframework.beans.factory.annotation.Autowired; import org.vaadin.addons.lazyquerycontainer.BeanQueryFactory; @@ -525,6 +525,10 @@ public class DistributionTable extends AbstractNamedVersionTable Date: Tue, 27 Sep 2016 10:40:38 +0200 Subject: [PATCH 27/43] JavaDoc and performance Signed-off-by: kaizimmerm --- .../repository/model/DistributionSet.java | 17 +++++++++++- .../jpa/model/JpaDistributionSet.java | 2 ++ .../ui/push/DelayedEventBusPushStrategy.java | 26 +++++++------------ 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSet.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSet.java index 5747b38f3..b0bff56cb 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSet.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSet.java @@ -26,10 +26,25 @@ import org.eclipse.hawkbit.repository.DistributionSetManagement; public interface DistributionSet extends NamedVersionedEntity { /** - * @return {@link Set} of assigned {@link DistributionSetTag}s. + * @return immutable {@link Set} of assigned {@link DistributionSetTag}s. */ Set getTags(); + /** + * @param tag + * to add + * @return true if tag could be added sucessfully (i.e. was not + * already in the list). + */ + boolean addTag(final DistributionSetTag tag); + + /** + * @param tag + * to remove + * @return true if tag was in the list and removed + */ + boolean removeTag(final DistributionSetTag tag); + /** * @return true if the set is deleted and only kept for history * purposes. diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSet.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSet.java index 8e12967c5..9e6efe826 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSet.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSet.java @@ -158,6 +158,7 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen return Collections.unmodifiableSet(tags); } + @Override public boolean addTag(final DistributionSetTag tag) { if (tags == null) { tags = new HashSet<>(); @@ -166,6 +167,7 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen return tags.add(tag); } + @Override public boolean removeTag(final DistributionSetTag tag) { if (tags == null) { return false; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/push/DelayedEventBusPushStrategy.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/push/DelayedEventBusPushStrategy.java index 5f72478cb..31de959ae 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/push/DelayedEventBusPushStrategy.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/push/DelayedEventBusPushStrategy.java @@ -59,7 +59,7 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { private static final int BLOCK_SIZE = 10_000; private final ScheduledExecutorService executorService; - private final BlockingDeque queue = new LinkedBlockingDeque<>(BLOCK_SIZE); + private final BlockingDeque queue = new LinkedBlockingDeque<>(BLOCK_SIZE); private final EventBus.SessionEventBus eventBus; private final com.google.common.eventbus.EventBus systemEventBus; private int uiid = -1; @@ -173,26 +173,20 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { @Override public void run() { - LOG.debug("UI EventBus aggregator started for UI {}", uiid); + LOG.debug("UI EventBus aggregator started for UI {}", vaadinUI.getUIId()); final long timestamp = System.currentTimeMillis(); final int size = queue.size(); if (size <= 0) { - LOG.debug("UI EventBus aggregator for UI {} has nothing to do.", uiid); + LOG.debug("UI EventBus aggregator for UI {} has nothing to do.", vaadinUI.getUIId()); return; } final List events = new ArrayList<>(size); - for (int i = 0; i < size; i++) { - final Event pollEvent = queue.poll(); - if (pollEvent == null) { - continue; - } - events.add(pollEvent); - } + final int eventsSize = queue.drainTo(events); if (events.isEmpty()) { - LOG.debug("UI EventBus aggregator for UI {} has nothing to do.", uiid); + LOG.debug("UI EventBus aggregator for UI {} has nothing to do.", vaadinUI.getUIId()); return; } @@ -201,15 +195,13 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { return; } - final int eventsSize = events.size(); - LOG.debug("UI EventBus aggregator dispatches {} events for session {} for UI {}", eventsSize, vaadinSession, - uiid); + vaadinUI.getUIId()); doDispatch(events, wrappedSession); LOG.debug("UI EventBus aggregator done with sending {} events in {} ms for UI {}", eventsSize, - System.currentTimeMillis() - timestamp, uiid); + System.currentTimeMillis() - timestamp, vaadinUI.getUIId()); } @@ -230,7 +222,7 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { LOG.debug("UI EventBus aggregator of UI {} left lock on session.", vaadinUI.getUIId()); }).get(); } catch (InterruptedException | ExecutionException e) { - LOG.error("Wait for Vaadin session for UI {} interrupted!", uiid, e); + LOG.error("Wait for Vaadin session for UI {} interrupted!", vaadinUI.getUIId(), e); } finally { SecurityContextHolder.setContext(oldContext); } @@ -240,7 +232,7 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { final Set> filterBulkEvenTypes = eventProvider.getFilteredBulkEventsType(events); for (final Class bulkType : filterBulkEvenTypes) { - final List listBulkEvents = events.stream() + final List listBulkEvents = events.stream() .filter(event -> DelayedEventBusPushStrategy.eventSecurityCheck(userContext, event) && bulkType.isInstance(event)) .collect(Collectors.toList()); From eb1efb3b5d6d7716fc19d508d463e40ca9017e48 Mon Sep 17 00:00:00 2001 From: kaizimmerm Date: Tue, 27 Sep 2016 10:41:45 +0200 Subject: [PATCH 28/43] Convert to lambda Signed-off-by: kaizimmerm --- .../PropertyHostnameResolverAutoConfiguration.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/url/PropertyHostnameResolverAutoConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/url/PropertyHostnameResolverAutoConfiguration.java index 0bd4a8240..83885ae08 100644 --- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/url/PropertyHostnameResolverAutoConfiguration.java +++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/url/PropertyHostnameResolverAutoConfiguration.java @@ -41,14 +41,11 @@ public class PropertyHostnameResolverAutoConfiguration { @Bean @ConditionalOnMissingBean(value = HostnameResolver.class) public HostnameResolver hostnameResolver() { - return new HostnameResolver() { - @Override - public URL resolveHostname() { - try { - return new URL(serverProperties.getUrl()); - } catch (final MalformedURLException e) { - throw Throwables.propagate(e); - } + return () -> { + try { + return new URL(serverProperties.getUrl()); + } catch (final MalformedURLException e) { + throw Throwables.propagate(e); } }; } From 9ee9ed16ce2bd5734b9f1d99d3dd053ce7b3b6f6 Mon Sep 17 00:00:00 2001 From: Michael Hirsch Date: Tue, 27 Sep 2016 16:42:48 +0200 Subject: [PATCH 29/43] hawkBit pom release preparations Signed-off-by: Michael Hirsch --- pom.xml | 144 +++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 128 insertions(+), 16 deletions(-) diff --git a/pom.xml b/pom.xml index 4684aa7b9..004c98e4e 100644 --- a/pom.xml +++ b/pom.xml @@ -51,6 +51,46 @@ ${release.scm.url} + + Hudson + https://hudson.eclipse.org/hawkbit/ + + + + + kaizimmerm + kai.zimmermann@bosch-si.com + Bosch Software Innovations GmbH + https://www.bosch-si.com + + Lead + Committer + + + + michahirsch + michael.hirsch@bosch-si.com + Bosch Software Innovations GmbH + https://www.bosch-si.com + + Committer + + + + + + + ossrh + hawkBit Repository - Release + https://oss.sonatype.org/service/local/staging/deploy/maven2 + + + ossrh + hawkBit Repository - Snapshots + https://oss.sonatype.org/content/repositories/snapshots + + + vaadin-addons @@ -60,14 +100,14 @@ 1.8 - 1.3.7.RELEASE + true - + 1.6.1.RELEASE 4.1.2.RELEASE - + 3.2.2 @@ -114,7 +154,7 @@ https://github.com/eclipse/hawkbit.git - + https://sonar.eu-gb.mybluemix.net eclipse/hawkbit https://projects.eclipse.org/projects/iot.hawkbit @@ -125,7 +165,7 @@ ${jacoco.outputDir}/${jacoco.out.ut.file} jacoco-it.exec ${jacoco.outputDir}/${jacoco.out.it.file} - + @@ -135,11 +175,27 @@ org.apache.maven.plugins maven-compiler-plugin - -Xlint:all - true - true + -Xlint:all + true + true - + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.5 + true + + ossrh + https://oss.sonatype.org/ + false + + com.mycila license-maven-plugin @@ -164,6 +220,32 @@ + + org.apache.maven.plugins + maven-enforcer-plugin + 1.4.1 + + + + enforce-no-snapshots + + enforce + + + ${snapshotDependencyAllowed} + + + No Snapshots Allowed! + + + No Snapshots Allowed! + + + + + + org.codehaus.mojo versions-maven-plugin @@ -218,7 +300,7 @@ - + @@ -238,7 +320,7 @@ - + @@ -252,7 +334,7 @@ - + @@ -323,13 +405,43 @@ ${jacoco.version} - org.bsc.maven - maven-processor-plugin - ${maven.processor.plugin.version} + org.bsc.maven + maven-processor-plugin + ${maven.processor.plugin.version} + + + + create_gpg_signature + + false + + createGPGSignature + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + + + + + + @@ -496,7 +608,7 @@ - + org.apache.commons commons-lang3 ${commons-lang3.version} From 5c53bef1644f69054fdb4a256b3c25c68d7fa3f0 Mon Sep 17 00:00:00 2001 From: Kai Zimmermann Date: Wed, 28 Sep 2016 09:18:03 +0200 Subject: [PATCH 30/43] Configurable download URL generation (#296) Configurable download URL generation. Signed-off-by: kaizimmerm --- .../simulator/DeviceSimulatorUpdater.java | 13 +- .../application-cloudsandbox.properties | 17 +- .../src/main/resources/application.properties | 6 - .../builder/SoftwareModuleTypeBuilder.java | 12 +- .../artifact/repository/ArtifactStore.java | 4 +- .../cache/CacheAutoConfiguration.java | 3 +- .../SecurityManagedConfiguration.java | 8 +- ...ertyHostnameResolverAutoConfiguration.java | 31 ++- .../main/resources/hawkbitdefaults.properties | 18 +- hawkbit-core/pom.xml | 10 + .../api/{UrlProtocol.java => ApiType.java} | 16 +- .../org/eclipse/hawkbit/api/ArtifactUrl.java | 109 ++++++++ .../hawkbit/api/ArtifactUrlHandler.java | 28 +- .../api/ArtifactUrlHandlerProperties.java | 185 ++++++++----- .../org/eclipse/hawkbit/api/Base62Util.java | 68 +++++ .../api/DefaultProtocolProperties.java | 79 ------ .../api/PropertyBasedArtifactUrlHandler.java | 114 ++++---- .../eclipse/hawkbit/api/URLPlaceholder.java | 247 ++++++++++++++++++ .../configuration/TenantConfigurationKey.java | 2 +- .../eclipse/hawkbit/api/Base62UtilTest.java | 40 +++ .../PropertyBasedArtifactUrlHandlerTest.java | 124 +++++++++ .../ddi/json/model/DdiCancelActionToStop.java | 1 - .../hawkbit/ddi/json/model/DdiChunk.java | 7 +- .../hawkbit/ddi/json/model/DdiConfig.java | 1 - .../hawkbit/ddi/json/model/DdiDeployment.java | 10 +- .../hawkbit/ddi/json/model/DdiResult.java | 2 +- .../hawkbit/ddi/json/model/DdiStatus.java | 9 +- .../rest/api/DdiRootControllerRestApi.java | 77 +++--- .../DdiDlArtifactStoreControllerRestApi.java | 12 +- .../rest/resource/DataConversionHelper.java | 52 ++-- .../ddi/rest/resource/DdiRootController.java | 110 ++++---- .../rest/resource/DdiDeploymentBaseTest.java | 110 +++----- .../resources/application-test.properties | 43 +++ .../AmqpAuthenticationMessageHandler.java | 236 +++++++++++++++++ .../hawkbit/amqp/AmqpConfiguration.java | 63 ++++- .../amqp/AmqpControllerAuthentication.java | 29 +- .../amqp/AmqpMessageDispatcherService.java | 65 +++-- .../amqp/AmqpMessageHandlerService.java | 235 ++--------------- .../eclipse/hawkbit/amqp/BaseAmqpService.java | 11 +- .../AmqpControllerAuthenticationTest.java | 202 +++++++++++--- .../AmqpMessageDispatcherServiceTest.java | 45 +++- .../amqp/AmqpMessageHandlerServiceTest.java | 44 ++-- .../hawkbit/amqp/BaseAmqpServiceTest.java | 4 +- .../PropertyBasedArtifactUrlHandlerTest.java | 95 ------- .../dmf/json/model/ActionUpdateStatus.java | 20 +- .../hawkbit/dmf/json/model/Artifact.java | 23 +- .../json/model/DownloadAndUpdateRequest.java | 15 +- .../dmf/json/model/SoftwareModule.java | 10 +- .../dmf/json/model/TenantSecurityToken.java | 126 ++++++++- ...actHttpControllerAuthenticationFilter.java | 4 +- .../hawkbit/mgmt/json/model/PagedList.java | 3 +- .../repository/ArtifactManagement.java | 11 +- .../repository/ControllerManagement.java | 63 +++-- .../DistributionSetAssignmentResult.java | 6 +- .../repository/DistributionSetManagement.java | 17 +- .../repository/SoftwareManagement.java | 29 +- .../hawkbit/repository/SystemManagement.java | 13 +- .../event/RolloutGroupCreatedEvent.java | 2 + .../TargetAssignDistributionSetEvent.java | 39 +-- .../model/AssignedSoftwareModule.java | 4 +- .../repository/model/AssignmentResult.java | 13 +- .../repository/model/EntityInterceptor.java | 3 + .../repository/jpa/JpaArtifactManagement.java | 3 +- .../jpa/JpaControllerManagement.java | 25 +- .../jpa/JpaDeploymentManagement.java | 7 +- .../repository/jpa/JpaSystemManagement.java | 5 + .../repository/jpa/JpaTagManagement.java | 2 +- .../jpa/model/JpaDistributionSetTag.java | 20 -- .../repository/jpa/model/JpaTarget.java | 4 +- .../repository/jpa/model/JpaTargetTag.java | 20 -- .../jpa/ArtifactManagementTest.java | 10 +- .../jpa/DeploymentManagementTest.java | 2 +- .../eclipse/hawkbit/TestConfiguration.java | 11 +- ...TenantUserPasswordAuthenticationToken.java | 31 +++ ...lerPreAuthenticateSecurityTokenFilter.java | 81 +++--- .../ui/artifacts/upload/UploadLayout.java | 4 +- .../dstable/DsMetadataPopupLayout.java | 1 - 77 files changed, 2114 insertions(+), 1110 deletions(-) rename hawkbit-core/src/main/java/org/eclipse/hawkbit/api/{UrlProtocol.java => ApiType.java} (64%) create mode 100644 hawkbit-core/src/main/java/org/eclipse/hawkbit/api/ArtifactUrl.java create mode 100644 hawkbit-core/src/main/java/org/eclipse/hawkbit/api/Base62Util.java delete mode 100644 hawkbit-core/src/main/java/org/eclipse/hawkbit/api/DefaultProtocolProperties.java create mode 100644 hawkbit-core/src/main/java/org/eclipse/hawkbit/api/URLPlaceholder.java create mode 100644 hawkbit-core/src/test/java/org/eclipse/hawkbit/api/Base62UtilTest.java create mode 100644 hawkbit-core/src/test/java/org/eclipse/hawkbit/api/PropertyBasedArtifactUrlHandlerTest.java create mode 100644 hawkbit-ddi-resource/src/test/resources/application-test.properties create mode 100644 hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpAuthenticationMessageHandler.java delete mode 100644 hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/util/PropertyBasedArtifactUrlHandlerTest.java diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DeviceSimulatorUpdater.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DeviceSimulatorUpdater.java index 01a9231e3..e73244247 100644 --- a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DeviceSimulatorUpdater.java +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DeviceSimulatorUpdater.java @@ -34,7 +34,6 @@ import org.apache.http.conn.ssl.SSLContextBuilder; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.eclipse.hawkbit.dmf.json.model.Artifact; -import org.eclipse.hawkbit.dmf.json.model.Artifact.UrlProtocol; import org.eclipse.hawkbit.dmf.json.model.SoftwareModule; import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol; import org.eclipse.hawkbit.simulator.UpdateStatus.ResponseStatus; @@ -206,12 +205,12 @@ public class DeviceSimulatorUpdater { private static void handleArtifacts(final String targetToken, final List status, final Artifact artifact) { - if (artifact.getUrls().containsKey(UrlProtocol.HTTPS)) { - status.add(downloadUrl(artifact.getUrls().get(UrlProtocol.HTTPS), targetToken, - artifact.getHashes().getSha1(), artifact.getSize())); - } else if (artifact.getUrls().containsKey(UrlProtocol.HTTP)) { - status.add(downloadUrl(artifact.getUrls().get(UrlProtocol.HTTP), targetToken, - artifact.getHashes().getSha1(), artifact.getSize())); + if (artifact.getUrls().containsKey("https")) { + status.add(downloadUrl(artifact.getUrls().get("https"), targetToken, artifact.getHashes().getSha1(), + artifact.getSize())); + } else if (artifact.getUrls().containsKey("http")) { + status.add(downloadUrl(artifact.getUrls().get("http"), targetToken, artifact.getHashes().getSha1(), + artifact.getSize())); } } diff --git a/examples/hawkbit-example-app/src/main/resources/application-cloudsandbox.properties b/examples/hawkbit-example-app/src/main/resources/application-cloudsandbox.properties index a0cad4e9b..85c47cb8f 100644 --- a/examples/hawkbit-example-app/src/main/resources/application-cloudsandbox.properties +++ b/examples/hawkbit-example-app/src/main/resources/application-cloudsandbox.properties @@ -9,8 +9,15 @@ vaadin.servlet.productionMode=true -hawkbit.artifact.url.coap.enabled=false -hawkbit.artifact.url.http.enabled=false -hawkbit.artifact.url.https.enabled=true -hawkbit.artifact.url.https.pattern={protocol}://{hostname}/{tenant}/controller/v1/{targetId}/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName} -hawkbit.artifact.url.https.hostname=hawkbit.eu-gb.mybluemix.net \ No newline at end of file +## Configuration for building download URLs - START +hawkbit.artifact.url.protocols.download-http.rel=download-http +hawkbit.artifact.url.protocols.download-http.protocol=https +hawkbit.artifact.url.protocols.download-http.supports=DMF,DDI +hawkbit.artifact.url.protocols.download-http.hostname=hawkbit.eu-gb.mybluemix.net +hawkbit.artifact.url.protocols.download-http.ref={protocol}://{hostname}/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName} +hawkbit.artifact.url.protocols.md5sum-http.rel=md5sum-http +hawkbit.artifact.url.protocols.md5sum-http.protocol=${hawkbit.artifact.url.protocols.download-http.protocol} +hawkbit.artifact.url.protocols.md5sum-http.supports=DDI +hawkbit.artifact.url.protocols.md5sum-http.hostname=${hawkbit.artifact.url.protocols.download-http.hostname} +hawkbit.artifact.url.protocols.md5sum-http.ref=${hawkbit.artifact.url.protocols.download-http.ref}.MD5SUM +## Configuration for building download URLs - END diff --git a/examples/hawkbit-example-app/src/main/resources/application.properties b/examples/hawkbit-example-app/src/main/resources/application.properties index 1a94206bb..0c32c8f8c 100644 --- a/examples/hawkbit-example-app/src/main/resources/application.properties +++ b/examples/hawkbit-example-app/src/main/resources/application.properties @@ -16,12 +16,6 @@ hawkbit.server.ddi.security.authentication.anonymous.enabled=true hawkbit.server.ddi.security.authentication.targettoken.enabled=true hawkbit.server.ddi.security.authentication.gatewaytoken.enabled=true -# Download URL generation config -hawkbit.artifact.url.coap.enabled=false -hawkbit.artifact.url.http.enabled=true -hawkbit.artifact.url.http.port=8080 -hawkbit.artifact.url.https.enabled=false - ## Vaadin configuration vaadin.servlet.productionMode=false diff --git a/examples/hawkbit-example-mgmt-feign-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/SoftwareModuleTypeBuilder.java b/examples/hawkbit-example-mgmt-feign-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/SoftwareModuleTypeBuilder.java index 7807d0f11..b8aee0f97 100644 --- a/examples/hawkbit-example-mgmt-feign-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/SoftwareModuleTypeBuilder.java +++ b/examples/hawkbit-example-mgmt-feign-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/SoftwareModuleTypeBuilder.java @@ -50,11 +50,21 @@ public class SoftwareModuleTypeBuilder { return this; } + /** + * @param description + * of the module + * @return the builder itself + */ public SoftwareModuleTypeBuilder description(final String description) { this.description = description; return this; } + /** + * @param maxAssignments + * of a module of that type to the same distribution set + * @return the builder itself + */ public SoftwareModuleTypeBuilder maxAssignments(final int maxAssignments) { this.maxAssignments = maxAssignments; return this; @@ -99,4 +109,4 @@ public class SoftwareModuleTypeBuilder { return body; } -} \ No newline at end of file +} diff --git a/hawkbit-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactStore.java b/hawkbit-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactStore.java index eabd2b329..4795ba7e7 100644 --- a/hawkbit-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactStore.java +++ b/hawkbit-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactStore.java @@ -227,7 +227,7 @@ public class ArtifactStore implements ArtifactRepository { * @return a paged list of artifacts mapped from the given dbFiles */ private List map(final List dbFiles) { - return dbFiles.stream().map(this::map).collect(Collectors.toList()); + return dbFiles.stream().map(ArtifactStore::map).collect(Collectors.toList()); } /** @@ -263,7 +263,7 @@ public class ArtifactStore implements ArtifactRepository { * the mongoDB gridFs file. * @return a mapped artifact from the given dbFile */ - private GridFsArtifact map(final GridFSFile fsFile) { + private static GridFsArtifact map(final GridFSFile fsFile) { if (fsFile == null) { return null; } diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/cache/CacheAutoConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/cache/CacheAutoConfiguration.java index a170dfcd3..2d61624cf 100644 --- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/cache/CacheAutoConfiguration.java +++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/cache/CacheAutoConfiguration.java @@ -82,8 +82,7 @@ public class CacheAutoConfiguration extends CachingConfigurerSupport { */ @Override public Collection resolveCaches(final CacheOperationInvocationContext context) { - return super.resolveCaches(context).stream().map(cache -> new TenantCacheWrapper(cache)) - .collect(Collectors.toList()); + return super.resolveCaches(context).stream().map(TenantCacheWrapper::new).collect(Collectors.toList()); } /* diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityManagedConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityManagedConfiguration.java index 9a7c7df52..565b1520a 100644 --- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityManagedConfiguration.java +++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityManagedConfiguration.java @@ -148,7 +148,7 @@ public class SecurityManagedConfiguration { private DdiSecurityProperties ddiSecurityConfiguration; @Autowired - private org.springframework.boot.autoconfigure.security.SecurityProperties springSecurityProperties; + private SecurityProperties springSecurityProperties; @Autowired private SystemSecurityContext systemSecurityContext; @@ -478,7 +478,7 @@ class TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler extends public void onAuthenticationSuccess(final Authentication authentication) throws Exception { if (authentication.getClass().equals(TenantUserPasswordAuthenticationToken.class)) { - systemSecurityContext.runAsSystemAsTenant(() -> systemManagement.getTenantMetadata(), + systemSecurityContext.runAsSystemAsTenant(systemManagement::getTenantMetadata, ((TenantUserPasswordAuthenticationToken) authentication).getTenant().toString()); } else if (authentication.getClass().equals(UsernamePasswordAuthenticationToken.class)) { // TODO: vaadin4spring-ext-security does not give us the @@ -489,7 +489,7 @@ class TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler extends // vaadin4spring 0.0.7 because it // has been fixed. final String defaultTenant = "DEFAULT"; - systemSecurityContext.runAsSystemAsTenant(() -> systemManagement.getTenantMetadata(), defaultTenant); + systemSecurityContext.runAsSystemAsTenant(systemManagement::getTenantMetadata, defaultTenant); } super.onAuthenticationSuccess(authentication); @@ -526,7 +526,7 @@ class AuthenticationSuccessTenantMetadataCreationFilter implements Filter { private void lazyCreateTenantMetadata() { final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication != null && authentication.isAuthenticated()) { - systemSecurityContext.runAsSystem(() -> systemManagement.getTenantMetadata()); + systemSecurityContext.runAsSystem(systemManagement::getTenantMetadata); } } diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/url/PropertyHostnameResolverAutoConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/url/PropertyHostnameResolverAutoConfiguration.java index 83885ae08..e95a0ec35 100644 --- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/url/PropertyHostnameResolverAutoConfiguration.java +++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/url/PropertyHostnameResolverAutoConfiguration.java @@ -12,8 +12,10 @@ import java.net.MalformedURLException; import java.net.URL; import org.eclipse.hawkbit.HawkbitServerProperties; +import org.eclipse.hawkbit.api.ArtifactUrlHandler; +import org.eclipse.hawkbit.api.ArtifactUrlHandlerProperties; import org.eclipse.hawkbit.api.HostnameResolver; -import org.springframework.beans.factory.annotation.Autowired; +import org.eclipse.hawkbit.api.PropertyBasedArtifactUrlHandler; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; @@ -22,25 +24,22 @@ import org.springframework.context.annotation.Configuration; import com.google.common.base.Throwables; /** - * Autoconfiguration of the {@link HostnameResolver} based on a property. - * - * - * + * Auto configuration for {@link HostnameResolver} and + * {@link ArtifactUrlHandler} based on a properties. */ @Configuration -@EnableConfigurationProperties(HawkbitServerProperties.class) +@EnableConfigurationProperties({ HawkbitServerProperties.class, ArtifactUrlHandlerProperties.class }) public class PropertyHostnameResolverAutoConfiguration { - @Autowired - private HawkbitServerProperties serverProperties; - /** + * @param serverProperties + * to get the servers URL * @return the default autoconfigure hostname resolver implementation which * is property based specified by the property {@link #url} */ @Bean @ConditionalOnMissingBean(value = HostnameResolver.class) - public HostnameResolver hostnameResolver() { + public HostnameResolver hostnameResolver(final HawkbitServerProperties serverProperties) { return () -> { try { return new URL(serverProperties.getUrl()); @@ -50,4 +49,16 @@ public class PropertyHostnameResolverAutoConfiguration { }; } + /** + * @param urlHandlerProperties + * for bean configuration + * @return PropertyBasedArtifactUrlHandler bean + */ + @Bean + @ConditionalOnMissingBean(ArtifactUrlHandler.class) + public PropertyBasedArtifactUrlHandler propertyBasedArtifactUrlHandler( + final ArtifactUrlHandlerProperties urlHandlerProperties) { + return new PropertyBasedArtifactUrlHandler(urlHandlerProperties); + } + } diff --git a/hawkbit-autoconfigure/src/main/resources/hawkbitdefaults.properties b/hawkbit-autoconfigure/src/main/resources/hawkbitdefaults.properties index f1e8c0a21..3ac45a027 100644 --- a/hawkbit-autoconfigure/src/main/resources/hawkbitdefaults.properties +++ b/hawkbit-autoconfigure/src/main/resources/hawkbitdefaults.properties @@ -41,9 +41,25 @@ hawkbit.controller.maxPollingTime=23:59:59 hawkbit.controller.minPollingTime=00:00:30 # Attention: if you want to use a maximumPollingTime greater 23:59:59 you have to update the DurationField in the configuration window - # Configuration for RabbitMQ integration hawkbit.dmf.rabbitmq.deadLetterQueue=dmf_connector_deadletter_ttl hawkbit.dmf.rabbitmq.deadLetterExchange=dmf.connector.deadletter hawkbit.dmf.rabbitmq.receiverQueue=dmf_receiver hawkbit.dmf.rabbitmq.authenticationReceiverQueue=authentication_receiver + +# Download URL generation configuration +hawkbit.artifact.url.protocols.download-http.rel=download-http +hawkbit.artifact.url.protocols.download-http.hostname=localhost +hawkbit.artifact.url.protocols.download-http.ip=127.0.0.1 +hawkbit.artifact.url.protocols.download-http.protocol=http +hawkbit.artifact.url.protocols.download-http.port=8080 +hawkbit.artifact.url.protocols.download-http.supports=DMF,DDI +hawkbit.artifact.url.protocols.download-http.ref={protocol}://{hostname}:{port}/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName} +hawkbit.artifact.url.protocols.md5sum-http.rel=md5sum-http +hawkbit.artifact.url.protocols.md5sum-http.protocol=${hawkbit.artifact.url.protocols.download-http.protocol} +hawkbit.artifact.url.protocols.md5sum-http.hostname=${hawkbit.artifact.url.protocols.download-http.hostname} +hawkbit.artifact.url.protocols.md5sum-http.ip=${hawkbit.artifact.url.protocols.download-http.ip} +hawkbit.artifact.url.protocols.md5sum-http.port=${hawkbit.artifact.url.protocols.download-http.port} +hawkbit.artifact.url.protocols.md5sum-http.supports=DDI +hawkbit.artifact.url.protocols.md5sum-http.ref=${hawkbit.artifact.url.protocols.download-http.ref}.MD5SUM + diff --git a/hawkbit-core/pom.xml b/hawkbit-core/pom.xml index f9e140d40..b8f339a01 100644 --- a/hawkbit-core/pom.xml +++ b/hawkbit-core/pom.xml @@ -33,6 +33,16 @@ guava + + org.easytesting + fest-assert-core + test + + + org.easytesting + fest-assert + test + org.springframework.boot spring-boot-starter-test diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/UrlProtocol.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/ApiType.java similarity index 64% rename from hawkbit-core/src/main/java/org/eclipse/hawkbit/api/UrlProtocol.java rename to hawkbit-core/src/main/java/org/eclipse/hawkbit/api/ApiType.java index 77c23ad0d..986bef476 100644 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/UrlProtocol.java +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/ApiType.java @@ -9,8 +9,18 @@ package org.eclipse.hawkbit.api; /** - * Represented the supported protocols for artifact url's. + * hawkBit API type. + * */ -public enum UrlProtocol { - COAP, HTTP, HTTPS +public enum ApiType { + + /** + * Support for Device Management Federation API. + */ + DMF, + + /** + * Support for Direct Device Integration API. + */ + DDI; } diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/ArtifactUrl.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/ArtifactUrl.java new file mode 100644 index 000000000..6d60d01e6 --- /dev/null +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/ArtifactUrl.java @@ -0,0 +1,109 @@ +/** + * 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.api; + +/** + * Container for a generated Artifact URL. + * + */ +public class ArtifactUrl { + + private final String protocol; + private final String rel; + private final String ref; + + /** + * Constructor. + * + * @param protocol + * string, e.g. ftp, http, https + * @param rel + * hypermedia value + * @param ref + * hypermedia value + */ + public ArtifactUrl(final String protocol, final String rel, final String ref) { + this.protocol = protocol; + this.rel = rel; + this.ref = ref; + } + + /** + * @return protocol name used in DMF API messages. + */ + public String getProtocol() { + return protocol; + } + + /** + * @return rel links value useful in hypermedia. + */ + public String getRel() { + return rel; + } + + /** + * @return generated artifact download URL + */ + public String getRef() { + return ref; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((protocol == null) ? 0 : protocol.hashCode()); + result = prime * result + ((ref == null) ? 0 : ref.hashCode()); + result = prime * result + ((rel == null) ? 0 : rel.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ArtifactUrl other = (ArtifactUrl) obj; + if (protocol == null) { + if (other.protocol != null) { + return false; + } + } else if (!protocol.equals(other.protocol)) { + return false; + } + if (ref == null) { + if (other.ref != null) { + return false; + } + } else if (!ref.equals(other.ref)) { + return false; + } + if (rel == null) { + if (other.rel != null) { + return false; + } + } else if (!rel.equals(other.rel)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "ArtifactUrl [protocol=" + protocol + ", rel=" + rel + ", ref=" + ref + "]"; + } + +} diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/ArtifactUrlHandler.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/ArtifactUrlHandler.java index 03492122c..8f2036c82 100644 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/ArtifactUrlHandler.java +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/ArtifactUrlHandler.java @@ -8,36 +8,26 @@ */ package org.eclipse.hawkbit.api; +import java.util.List; + /** * Interface declaration of the {@link ArtifactUrlHandler} which generates the * URLs to specific artifacts. * */ +@FunctionalInterface public interface ArtifactUrlHandler { /** * Returns a generated download URL for a given artifact parameters for a * specific protocol. * - * @param controllerId - * the authenticated controller id - * @param softwareModuleId - * the softwareModuleId belonging to the artifact - * @param filename - * the filename of the artifact - * @param sha1Hash - * the sha1Hash of the artifact - * @param protocol - * the protocol the URL should be generated + * @param placeholder + * data for URL generation + * @param api + * given protocol that URL needs to support + * * @return an URL for the given artifact parameters in a given protocol */ - String getUrl(String controllerId, final Long softwareModuleId, final String filename, final String sha1Hash, - final UrlProtocol protocol); - - /** - * @param protocol - * to check support for - * @return true of the handler supports given protocol. - */ - boolean protocolSupported(UrlProtocol protocol); + List getUrls(URLPlaceholder placeholder, ApiType api); } diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/ArtifactUrlHandlerProperties.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/ArtifactUrlHandlerProperties.java index 490b2d102..86b9e7d79 100644 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/ArtifactUrlHandlerProperties.java +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/ArtifactUrlHandlerProperties.java @@ -8,90 +8,153 @@ */ package org.eclipse.hawkbit.api; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import org.springframework.boot.context.properties.ConfigurationProperties; +import com.google.common.collect.Lists; + /** * Artifact handler properties class for holding all supported protocols with * host, ip, port and download pattern. + * + * @see PropertyBasedArtifactUrlHandler */ @ConfigurationProperties("hawkbit.artifact.url") public class ArtifactUrlHandlerProperties { - private final Http http = new Http(); - private final Https https = new Https(); - private final Coap coap = new Coap(); - - public Http getHttp() { - return http; - } - - public Https getHttps() { - return https; - } - - public Coap getCoap() { - return coap; - } + /** + * Rel as key and complete protocol as value. + */ + private final Map protocols = new HashMap<>(); /** - * @param protocol - * the protocol schema to retrieve the properties. - * @return the properties to a protocol or {@code null} if protocol does not - * have properties or protocol not supported + * Protocol specific properties to generate URLs accordingly. + * */ - public ProtocolProperties getProperties(final String protocol) { - switch (protocol) { - case "http": - return getHttp(); - case "https": - return getHttps(); - case "coap": - return getCoap(); - default: - return null; - } - } + public static class UrlProtocol { - /** - * Object to hold the properties for the HTTP protocol. - */ - public static class Http extends DefaultProtocolProperties { + private static final int DEFAULT_HTTP_PORT = 8080; /** - * Constructor. + * Set to true if enabled. */ - public Http() { - setPattern( - "{protocol}://{hostname}:{port}/{tenant}/controller/v1/{targetId}/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName}"); - } - } - - /** - * Object to hold the properties for the HTTP protocol. - */ - public static class Https extends DefaultProtocolProperties { + private boolean enabled = true; /** - * Constructor. + * Hypermedia rel value for this protocol. */ - public Https() { - setPattern( - "{protocol}://{hostname}:{port}/{tenant}/controller/v1/{targetId}/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName}"); - } - } - - /** - * Object to hold the properties for the HTTP protocol. - */ - public static class Coap extends DefaultProtocolProperties { + private String rel = "download-http"; /** - * Constructor. + * Hypermedia ref pattern for this protocol. Supported place holders are + * protocol,controllerId,targetId,targetIdBase62,ip,port,hostname, + * artifactFileName,artifactSHA1, + * artifactIdBase62,artifactId,tenant,softwareModuleId, + * softwareModuleIdBase62. + * + * The update server itself supports */ - public Coap() { - setPattern("{protocol}://{ip}:{port}/fw/{tenant}/{targetId}/sha1/{artifactSHA1}"); - setPort("5683"); + private String ref = "{protocol}://{hostname}:{port}/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName}"; + + /** + * Protocol name placeholder that can be used in ref pattern. + */ + private String protocol = "http"; + + /** + * Hostname placeholder that can be used in ref pattern. + */ + private String hostname = "localhost"; + + /** + * IP address placeholder that can be used in ref pattern. + */ + // Exception squid:S1313 - default only, can be configured + @SuppressWarnings("squid:S1313") + private String ip = "127.0.0.1"; + + /** + * Port placeholder that can be used in ref pattern. + */ + private Integer port = DEFAULT_HTTP_PORT; + + /** + * Support for the following hawkBit API. + */ + private List supports = Lists.newArrayList(ApiType.DDI, ApiType.DMF); + + public boolean isEnabled() { + return enabled; } + + public void setEnabled(final boolean enabled) { + this.enabled = enabled; + } + + public String getRel() { + return rel; + } + + public void setRel(final String rel) { + this.rel = rel; + } + + public String getRef() { + return ref; + } + + public void setRef(final String ref) { + this.ref = ref; + } + + public String getHostname() { + return hostname; + } + + public void setHostname(final String hostname) { + this.hostname = hostname; + } + + public String getIp() { + return ip; + } + + public void setIp(final String ip) { + this.ip = ip; + } + + public Integer getPort() { + return port; + } + + public void setPort(final Integer port) { + this.port = port; + } + + public List getSupports() { + return Collections.unmodifiableList(supports); + } + + public void setSupports(final List supports) { + this.supports = Collections.unmodifiableList(supports); + } + + public String getProtocol() { + return protocol; + } + + public void setProtocol(final String protocol) { + this.protocol = protocol; + } + + } + + public Map getProtocols() { + return protocols; } } diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/Base62Util.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/Base62Util.java new file mode 100644 index 000000000..37952b6a2 --- /dev/null +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/Base62Util.java @@ -0,0 +1,68 @@ +/** + * 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.api; + +/** + * Utility class for Base10 to Base62 conversion and vice versa. Base62 has the + * benefit of being shorter in ASCII representation than Base10. + */ +public final class Base62Util { + private static final String BASE62_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + private static final int BASE62_BASE = BASE62_ALPHABET.length(); + + private Base62Util() { + // Utility class + } + + /** + * @param base10 + * number + * @return converted number into Base62 ASCII string + */ + public static String fromBase10(final long base10) { + if (base10 == 0) { + return "0"; + } + + long temp = base10; + final StringBuilder sb = new StringBuilder(); + + while (temp > 0) { + temp = fromBase10(temp, sb); + } + return sb.reverse().toString(); + } + + /** + * @param base62 + * number + * @return converted number into Base10 + */ + public static Long toBase10(final String base62) { + return toBase10(new StringBuilder(base62).reverse().toString().toCharArray()); + } + + private static Long fromBase10(final long base10, final StringBuilder sb) { + final int rem = (int) (base10 % BASE62_BASE); + sb.append(BASE62_ALPHABET.charAt(rem)); + return base10 / BASE62_BASE; + } + + private static Long toBase10(final char[] chars) { + long base10 = 0L; + for (int i = chars.length - 1; i >= 0; i--) { + base10 += toBase10(BASE62_ALPHABET.indexOf(chars[i]), i); + } + return base10; + } + + private static int toBase10(final int n, final int pow) { + return n * (int) Math.pow(BASE62_BASE, pow); + } +} diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/DefaultProtocolProperties.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/DefaultProtocolProperties.java deleted file mode 100644 index 07eac8db1..000000000 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/DefaultProtocolProperties.java +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -package org.eclipse.hawkbit.api; - -/** - * Object to hold the properties for the base protocols. - */ -public class DefaultProtocolProperties implements ProtocolProperties { - // The IP address is not hardcoded. It's the default value, if the IP - // address is not configured. - @SuppressWarnings("squid:S1313") - private static final String DEFAULT_IP_LOCALHOST = "127.0.0.1"; - private static final String LOCALHOST = "localhost"; - - private String hostname = LOCALHOST; - private String ip = DEFAULT_IP_LOCALHOST; - private String port = ""; - /** - * An ant-URL pattern with placeholder to build the URL on. The URL can have - * specific artifact placeholder. - */ - private String pattern; - - /** - * Enables protocol. - */ - private boolean enabled = true; - - @Override - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(final boolean enabled) { - this.enabled = enabled; - } - - @Override - public String getHostname() { - return hostname; - } - - public void setHostname(final String hostname) { - this.hostname = hostname; - } - - @Override - public String getIp() { - return ip; - } - - public void setIp(final String ip) { - this.ip = ip; - } - - @Override - public String getPattern() { - return pattern; - } - - public void setPattern(final String urlPattern) { - this.pattern = urlPattern; - } - - @Override - public String getPort() { - return port; - } - - public void setPort(final String port) { - this.port = port; - } -} diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/PropertyBasedArtifactUrlHandler.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/PropertyBasedArtifactUrlHandler.java index 91a271541..229438477 100644 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/PropertyBasedArtifactUrlHandler.java +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/PropertyBasedArtifactUrlHandler.java @@ -9,55 +9,80 @@ package org.eclipse.hawkbit.api; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.stream.Collectors; -import org.eclipse.hawkbit.tenancy.TenantAware; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.stereotype.Component; +import org.eclipse.hawkbit.api.ArtifactUrlHandlerProperties.UrlProtocol; import com.google.common.base.Strings; import com.google.common.net.UrlEscapers; /** * Implementation for ArtifactUrlHandler for creating urls to download resource - * based on pattern. + * based on patterns configured by {@link ArtifactUrlHandlerProperties}. + * + * This mechanism can be used to generate links to arbitrary file hosting + * infrastructure. However, the hawkBit update server supports hosting files as + * well in the following {@link UrlProtocol#getRef()} patterns: + * + * Default: + * {protocol}://{hostname}:{port}/{tenant}/controller/v1/{controllerId}/ + * softwaremodules/{softwareModuleId}/artifacts/{artifactFileName} + * + * Default (MD5SUM files): + * {protocol}://{hostname}:{port}/{tenant}/controller/v1/{controllerId}/ + * softwaremodules/{softwareModuleId}/artifacts/{artifactFileName}.MD5SUM + * */ -@Component -@EnableConfigurationProperties(ArtifactUrlHandlerProperties.class) public class PropertyBasedArtifactUrlHandler implements ArtifactUrlHandler { private static final String PROTOCOL_PLACEHOLDER = "protocol"; - private static final String TARGET_ID_PLACEHOLDER = "targetId"; + private static final String CONTROLLER_ID_PLACEHOLDER = "controllerId"; + private static final String TARGET_ID_BASE10_PLACEHOLDER = "targetId"; + private static final String TARGET_ID_BASE62_PLACEHOLDER = "targetIdBase62"; private static final String IP_PLACEHOLDER = "ip"; private static final String PORT_PLACEHOLDER = "port"; private static final String HOSTNAME_PLACEHOLDER = "hostname"; private static final String ARTIFACT_FILENAME_PLACEHOLDER = "artifactFileName"; private static final String ARTIFACT_SHA1_PLACEHOLDER = "artifactSHA1"; + private static final String ARTIFACT_ID_BASE10_PLACEHOLDER = "artifactId"; + private static final String ARTIFACT_ID_BASE62_PLACEHOLDER = "artifactIdBase62"; private static final String TENANT_PLACEHOLDER = "tenant"; - private static final String SOFTWARE_MODULE_ID_PLACDEHOLDER = "softwareModuleId"; + private static final String TENANT_ID_BASE10_PLACEHOLDER = "tenantId"; + private static final String TENANT_ID_BASE62_PLACEHOLDER = "tenantIdBase62"; + private static final String SOFTWARE_MODULE_ID_BASE10_PLACDEHOLDER = "softwareModuleId"; + private static final String SOFTWARE_MODULE_ID_BASE62_PLACDEHOLDER = "softwareModuleIdBase62"; - @Autowired - private ArtifactUrlHandlerProperties urlHandlerProperties; + private final ArtifactUrlHandlerProperties urlHandlerProperties; - @Autowired - private TenantAware tenantAware; + /** + * @param urlHandlerProperties + * for URL generation configuration + */ + public PropertyBasedArtifactUrlHandler(final ArtifactUrlHandlerProperties urlHandlerProperties) { + this.urlHandlerProperties = urlHandlerProperties; + } @Override - public String getUrl(final String targetId, final Long softwareModuleId, final String filename, - final String sha1Hash, final UrlProtocol protocol) { + public List getUrls(final URLPlaceholder placeholder, final ApiType api) { - final String protocolString = protocol.name().toLowerCase(); - final ProtocolProperties properties = urlHandlerProperties.getProperties(protocolString); - if (properties == null || properties.getPattern() == null) { - return null; - } + return urlHandlerProperties.getProtocols().entrySet().stream() + .filter(entry -> entry.getValue().getSupports().contains(api)) + .filter(entry -> entry.getValue().isEnabled()) + .map(entry -> new ArtifactUrl(entry.getValue().getProtocol(), entry.getValue().getRel(), + generateUrl(entry.getValue(), placeholder))) + .collect(Collectors.toList()); + + } + + private static String generateUrl(final UrlProtocol protocol, final URLPlaceholder placeholder) { + final Set> entrySet = getReplaceMap(protocol, placeholder).entrySet(); + + String urlPattern = protocol.getRef(); - String urlPattern = properties.getPattern(); - final Set> entrySet = getReplaceMap(targetId, softwareModuleId, - UrlEscapers.urlFragmentEscaper().escape(filename), sha1Hash, protocolString, properties).entrySet(); for (final Entry entry : entrySet) { if (entry.getKey().equals(PORT_PLACEHOLDER)) { urlPattern = urlPattern.replace(":{" + entry.getKey() + "}", @@ -69,30 +94,29 @@ public class PropertyBasedArtifactUrlHandler implements ArtifactUrlHandler { return urlPattern; } - private Map getReplaceMap(final String targetId, final Long softwareModuleId, final String filename, - final String sha1Hash, final String protocol, final ProtocolProperties properties) { + private static Map getReplaceMap(final UrlProtocol protocol, final URLPlaceholder placeholder) { final Map replaceMap = new HashMap<>(); - replaceMap.put(IP_PLACEHOLDER, properties.getIp()); - replaceMap.put(HOSTNAME_PLACEHOLDER, properties.getHostname()); - replaceMap.put(ARTIFACT_FILENAME_PLACEHOLDER, filename); - replaceMap.put(ARTIFACT_SHA1_PLACEHOLDER, sha1Hash); - replaceMap.put(PROTOCOL_PLACEHOLDER, protocol); - replaceMap.put(PORT_PLACEHOLDER, properties.getPort()); - replaceMap.put(TENANT_PLACEHOLDER, tenantAware.getCurrentTenant()); - replaceMap.put(TARGET_ID_PLACEHOLDER, targetId); - replaceMap.put(SOFTWARE_MODULE_ID_PLACDEHOLDER, String.valueOf(softwareModuleId)); + replaceMap.put(IP_PLACEHOLDER, protocol.getIp()); + replaceMap.put(HOSTNAME_PLACEHOLDER, protocol.getHostname()); + replaceMap.put(ARTIFACT_FILENAME_PLACEHOLDER, + UrlEscapers.urlFragmentEscaper().escape(placeholder.getSoftwareData().getFilename())); + replaceMap.put(ARTIFACT_SHA1_PLACEHOLDER, placeholder.getSoftwareData().getSha1Hash()); + replaceMap.put(PROTOCOL_PLACEHOLDER, protocol.getProtocol()); + replaceMap.put(PORT_PLACEHOLDER, protocol.getPort() == null ? null : String.valueOf(protocol.getPort())); + replaceMap.put(TENANT_PLACEHOLDER, placeholder.getTenant()); + replaceMap.put(TENANT_ID_BASE10_PLACEHOLDER, String.valueOf(placeholder.getTenantId())); + replaceMap.put(TENANT_ID_BASE62_PLACEHOLDER, Base62Util.fromBase10(placeholder.getTenantId())); + replaceMap.put(CONTROLLER_ID_PLACEHOLDER, placeholder.getControllerId()); + replaceMap.put(TARGET_ID_BASE10_PLACEHOLDER, String.valueOf(placeholder.getTargetId())); + replaceMap.put(TARGET_ID_BASE62_PLACEHOLDER, Base62Util.fromBase10(placeholder.getTargetId())); + replaceMap.put(ARTIFACT_ID_BASE62_PLACEHOLDER, + Base62Util.fromBase10(placeholder.getSoftwareData().getArtifactId())); + replaceMap.put(ARTIFACT_ID_BASE10_PLACEHOLDER, String.valueOf(placeholder.getSoftwareData().getArtifactId())); + replaceMap.put(SOFTWARE_MODULE_ID_BASE10_PLACDEHOLDER, + String.valueOf(placeholder.getSoftwareData().getSoftwareModuleId())); + replaceMap.put(SOFTWARE_MODULE_ID_BASE62_PLACDEHOLDER, + Base62Util.fromBase10(placeholder.getSoftwareData().getSoftwareModuleId())); return replaceMap; } - @Override - public boolean protocolSupported(final UrlProtocol protocol) { - final String protocolString = protocol.name().toLowerCase(); - final ProtocolProperties properties = urlHandlerProperties.getProperties(protocolString); - if (properties == null || properties.getPattern() == null) { - return false; - } - - return properties.isEnabled(); - } - } diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/URLPlaceholder.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/URLPlaceholder.java new file mode 100644 index 000000000..43d51db5c --- /dev/null +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/URLPlaceholder.java @@ -0,0 +1,247 @@ +/** + * 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.api; + +/** + * Container for variables available to the {@link ArtifactUrlHandler}. + * + */ +public class URLPlaceholder { + private final String tenant; + private final Long tenantId; + private final String controllerId; + private final Long targetId; + private final SoftwareData softwareData; + + /** + * Constructor. + * + * @param tenant + * of the client + * @param tenantId + * of teh tenant + * @param controllerId + * of the target + * @param targetId + * of the target + * @param softwareData + * information about the artifact and software module that can be + * accessed by the URL. + */ + public URLPlaceholder(final String tenant, final Long tenantId, final String controllerId, final Long targetId, + final SoftwareData softwareData) { + this.tenant = tenant; + this.tenantId = tenantId; + this.controllerId = controllerId; + this.targetId = targetId; + this.softwareData = softwareData; + } + + /** + * Information about the artifact and software module that can be accessed + * by the URL. + * + */ + public static class SoftwareData { + private Long softwareModuleId; + private String filename; + private Long artifactId; + private String sha1Hash; + + /** + * Constructor. + * + * @param softwareModuleId + * of the module the artifact belongs to + * @param filename + * of the artifact + * @param artifactId + * of the artifact + * @param sha1Hash + * of the artifact + */ + public SoftwareData(final Long softwareModuleId, final String filename, final Long artifactId, + final String sha1Hash) { + this.softwareModuleId = softwareModuleId; + this.filename = filename; + this.artifactId = artifactId; + this.sha1Hash = sha1Hash; + } + + public Long getSoftwareModuleId() { + return softwareModuleId; + } + + public void setSoftwareModuleId(final Long softwareModuleId) { + this.softwareModuleId = softwareModuleId; + } + + public String getFilename() { + return filename; + } + + public void setFilename(final String filename) { + this.filename = filename; + } + + public Long getArtifactId() { + return artifactId; + } + + public void setArtifactId(final Long artifactId) { + this.artifactId = artifactId; + } + + public String getSha1Hash() { + return sha1Hash; + } + + public void setSha1Hash(final String sha1Hash) { + this.sha1Hash = sha1Hash; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((artifactId == null) ? 0 : artifactId.hashCode()); + result = prime * result + ((filename == null) ? 0 : filename.hashCode()); + result = prime * result + ((sha1Hash == null) ? 0 : sha1Hash.hashCode()); + result = prime * result + ((softwareModuleId == null) ? 0 : softwareModuleId.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final SoftwareData other = (SoftwareData) obj; + if (artifactId == null) { + if (other.artifactId != null) { + return false; + } + } else if (!artifactId.equals(other.artifactId)) { + return false; + } + if (filename == null) { + if (other.filename != null) { + return false; + } + } else if (!filename.equals(other.filename)) { + return false; + } + if (sha1Hash == null) { + if (other.sha1Hash != null) { + return false; + } + } else if (!sha1Hash.equals(other.sha1Hash)) { + return false; + } + if (softwareModuleId == null) { + if (other.softwareModuleId != null) { + return false; + } + } else if (!softwareModuleId.equals(other.softwareModuleId)) { + return false; + } + return true; + } + + } + + public String getTenant() { + return tenant; + } + + public Long getTenantId() { + return tenantId; + } + + public String getControllerId() { + return controllerId; + } + + public Long getTargetId() { + return targetId; + } + + public SoftwareData getSoftwareData() { + return softwareData; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((controllerId == null) ? 0 : controllerId.hashCode()); + result = prime * result + ((softwareData == null) ? 0 : softwareData.hashCode()); + result = prime * result + ((targetId == null) ? 0 : targetId.hashCode()); + result = prime * result + ((tenant == null) ? 0 : tenant.hashCode()); + result = prime * result + ((tenantId == null) ? 0 : tenantId.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final URLPlaceholder other = (URLPlaceholder) obj; + if (controllerId == null) { + if (other.controllerId != null) { + return false; + } + } else if (!controllerId.equals(other.controllerId)) { + return false; + } + if (softwareData == null) { + if (other.softwareData != null) { + return false; + } + } else if (!softwareData.equals(other.softwareData)) { + return false; + } + if (targetId == null) { + if (other.targetId != null) { + return false; + } + } else if (!targetId.equals(other.targetId)) { + return false; + } + if (tenant == null) { + if (other.tenant != null) { + return false; + } + } else if (!tenant.equals(other.tenant)) { + return false; + } + if (tenantId == null) { + if (other.tenantId != null) { + return false; + } + } else if (!tenantId.equals(other.tenantId)) { + return false; + } + return true; + } + +} diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/TenantConfigurationKey.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/TenantConfigurationKey.java index d68e963be..d5995da59 100644 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/TenantConfigurationKey.java +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/TenantConfigurationKey.java @@ -90,7 +90,7 @@ public enum TenantConfigurationKey { * @param validator * Validator which validates, that property is of correct format */ - private TenantConfigurationKey(final String key, final String defaultKeyName, final Class dataType, + TenantConfigurationKey(final String key, final String defaultKeyName, final Class dataType, final String defaultValue, final Class validator) { this.keyName = key; this.dataType = dataType; diff --git a/hawkbit-core/src/test/java/org/eclipse/hawkbit/api/Base62UtilTest.java b/hawkbit-core/src/test/java/org/eclipse/hawkbit/api/Base62UtilTest.java new file mode 100644 index 000000000..c9c11d10c --- /dev/null +++ b/hawkbit-core/src/test/java/org/eclipse/hawkbit/api/Base62UtilTest.java @@ -0,0 +1,40 @@ +/** + * 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.api; + +import static org.fest.assertions.api.Assertions.assertThat; + +import org.junit.Test; + +import ru.yandex.qatools.allure.annotations.Description; +import ru.yandex.qatools.allure.annotations.Features; +import ru.yandex.qatools.allure.annotations.Stories; + +@Features("Unit Tests - Artifact URL Handler") +@Stories("Base62 Utility tests") +public class Base62UtilTest { + + @Test + @Description("Convert Base10 numbres to Base62 ASCII strings.") + public void fromBase10() { + assertThat(Base62Util.fromBase10(0L)).isEqualTo("0"); + assertThat(Base62Util.fromBase10(11L)).isEqualTo("B"); + assertThat(Base62Util.fromBase10(36L)).isEqualTo("a"); + assertThat(Base62Util.fromBase10(999L)).isEqualTo("G7"); + } + + @Test + @Description("Convert Base62 ASCII strings to Base10 numbers.") + public void toBase10() { + assertThat(Base62Util.toBase10("0")).isEqualTo(0L); + assertThat(Base62Util.toBase10("B")).isEqualTo(11); + assertThat(Base62Util.toBase10("a")).isEqualTo(36L); + assertThat(Base62Util.toBase10("G7")).isEqualTo(999L); + } +} diff --git a/hawkbit-core/src/test/java/org/eclipse/hawkbit/api/PropertyBasedArtifactUrlHandlerTest.java b/hawkbit-core/src/test/java/org/eclipse/hawkbit/api/PropertyBasedArtifactUrlHandlerTest.java new file mode 100644 index 000000000..3d7fc18af --- /dev/null +++ b/hawkbit-core/src/test/java/org/eclipse/hawkbit/api/PropertyBasedArtifactUrlHandlerTest.java @@ -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.api; + +import static org.fest.assertions.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; + +import java.util.List; + +import org.eclipse.hawkbit.api.ArtifactUrlHandlerProperties.UrlProtocol; +import org.eclipse.hawkbit.api.URLPlaceholder.SoftwareData; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; + +import com.google.common.collect.Lists; + +import ru.yandex.qatools.allure.annotations.Description; +import ru.yandex.qatools.allure.annotations.Features; +import ru.yandex.qatools.allure.annotations.Stories; + +/** + * Tests for creating urls to download artifacts. + */ +@Features("Unit Tests - Artifact URL Handler") +@Stories("Test to generate the artifact download URL") +@RunWith(MockitoJUnitRunner.class) +public class PropertyBasedArtifactUrlHandlerTest { + + private static final String TEST_PROTO = "coap"; + private static final String TEST_REL = "download-udp"; + + private static final long TENANT_ID = 456789L; + private static final String CONTROLLER_ID = "Test"; + private static final String FILENAME = "Afile1234"; + private static final long SOFTWAREMODULEID = 87654L; + private static final long TARGETID = 3474366L; + private static final String TARGETID_BASE62 = "EZqA"; + private static final String SHA1HASH = "test12345"; + private static final long ARTIFACTID = 1345678L; + private static final String ARTIFACTID_BASE62 = "5e4U"; + private static final String TENANT = "TEST_TENANT"; + + private static final String HTTP_LOCALHOST = "http://localhost:8080/"; + + private ArtifactUrlHandler urlHandlerUnderTest; + + private ArtifactUrlHandlerProperties properties; + + private static URLPlaceholder placeholder = new URLPlaceholder(TENANT, TENANT_ID, CONTROLLER_ID, TARGETID, + new SoftwareData(SOFTWAREMODULEID, FILENAME, ARTIFACTID, SHA1HASH)); + + @Before + public void setup() { + properties = new ArtifactUrlHandlerProperties(); + urlHandlerUnderTest = new PropertyBasedArtifactUrlHandler(properties); + + } + + @Test + @Description("Tests the generation of http download url.") + public void urlGenerationWithDefaultConfiguration() { + properties.getProtocols().put("download-http", new UrlProtocol()); + + final List ddiUrls = urlHandlerUnderTest.getUrls(placeholder, ApiType.DDI); + assertEquals( + Lists.newArrayList(new ArtifactUrl("http", "download-http", HTTP_LOCALHOST + TENANT + "/controller/v1/" + + CONTROLLER_ID + "/softwaremodules/" + SOFTWAREMODULEID + "/artifacts/" + FILENAME)), + ddiUrls); + + final List dmfUrls = urlHandlerUnderTest.getUrls(placeholder, ApiType.DMF); + assertEquals(ddiUrls, dmfUrls); + } + + @Test + @Description("Tests the generation of custom download url with a CoAP example that supports DMF only.") + public void urlGenerationWithCustomConfiguration() { + final UrlProtocol proto = new UrlProtocol(); + proto.setIp("127.0.0.1"); + proto.setPort(5683); + proto.setProtocol(TEST_PROTO); + proto.setRel(TEST_REL); + proto.setSupports(Lists.newArrayList(ApiType.DMF)); + proto.setRef("{protocol}://{ip}:{port}/fw/{tenant}/{controllerId}/sha1/{artifactSHA1}"); + properties.getProtocols().put(TEST_PROTO, proto); + + List urls = urlHandlerUnderTest.getUrls(placeholder, ApiType.DDI); + + assertThat(urls).isEmpty(); + urls = urlHandlerUnderTest.getUrls(placeholder, ApiType.DMF); + + assertEquals(Lists.newArrayList(new ArtifactUrl(TEST_PROTO, TEST_REL, + "coap://127.0.0.1:5683/fw/" + TENANT + "/" + CONTROLLER_ID + "/sha1/" + SHA1HASH)), urls); + } + + @Test + @Description("Tests the generation of custom download url using Base62 references with a CoAP example that supports DMF only.") + public void urlGenerationWithCustomShortConfiguration() { + final UrlProtocol proto = new UrlProtocol(); + proto.setIp("127.0.0.1"); + proto.setPort(5683); + proto.setProtocol(TEST_PROTO); + proto.setRel(TEST_REL); + proto.setSupports(Lists.newArrayList(ApiType.DMF)); + proto.setRef("{protocol}://{ip}:{port}/fws/{tenant}/{targetIdBase62}/{artifactIdBase62}"); + properties.getProtocols().put("ftp", proto); + + List urls = urlHandlerUnderTest.getUrls(placeholder, ApiType.DDI); + + assertThat(urls).isEmpty(); + urls = urlHandlerUnderTest.getUrls(placeholder, ApiType.DMF); + + assertEquals(Lists.newArrayList(new ArtifactUrl(TEST_PROTO, TEST_REL, + TEST_PROTO + "://127.0.0.1:5683/fws/" + TENANT + "/" + TARGETID_BASE62 + "/" + ARTIFACTID_BASE62)), + urls); + } +} diff --git a/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiCancelActionToStop.java b/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiCancelActionToStop.java index 3ab8d6b55..45932ff25 100644 --- a/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiCancelActionToStop.java +++ b/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiCancelActionToStop.java @@ -25,7 +25,6 @@ public class DdiCancelActionToStop { * ID of the action to be stoppedW */ public DdiCancelActionToStop(final String stopId) { - super(); this.stopId = stopId; } diff --git a/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiChunk.java b/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiChunk.java index cf146b597..8f1172c39 100644 --- a/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiChunk.java +++ b/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiChunk.java @@ -8,6 +8,7 @@ */ package org.eclipse.hawkbit.ddi.json.model; +import java.util.Collections; import java.util.List; import javax.validation.constraints.NotNull; @@ -65,7 +66,11 @@ public class DdiChunk { } public List getArtifacts() { - return artifacts; + if (artifacts == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(artifacts); } } diff --git a/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiConfig.java b/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiConfig.java index 0dfed6e79..d01b9f277 100644 --- a/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiConfig.java +++ b/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiConfig.java @@ -30,7 +30,6 @@ public class DdiConfig { * configuration of the SP target */ public DdiConfig(final DdiPolling polling) { - super(); this.polling = polling; } diff --git a/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiDeployment.java b/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiDeployment.java index c7e364b36..2f7fd593e 100644 --- a/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiDeployment.java +++ b/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiDeployment.java @@ -8,6 +8,7 @@ */ package org.eclipse.hawkbit.ddi.json.model; +import java.util.Collections; import java.util.List; import com.fasterxml.jackson.annotation.JsonValue; @@ -41,7 +42,6 @@ public class DdiDeployment { * to handle. */ public DdiDeployment(final HandlingType download, final HandlingType update, final List chunks) { - super(); this.download = download; this.update = update; this.chunks = chunks; @@ -56,7 +56,11 @@ public class DdiDeployment { } public List getChunks() { - return chunks; + if (chunks == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(chunks); } /** @@ -81,7 +85,7 @@ public class DdiDeployment { private String name; - private HandlingType(final String name) { + HandlingType(final String name) { this.name = name; } diff --git a/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiResult.java b/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiResult.java index 36f0d134c..6d5085569 100644 --- a/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiResult.java +++ b/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiResult.java @@ -71,7 +71,7 @@ public class DdiResult { private String name; - private FinalResult(final String name) { + FinalResult(final String name) { this.name = name; } diff --git a/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiStatus.java b/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiStatus.java index cb9b57187..140ba08e0 100644 --- a/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiStatus.java +++ b/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiStatus.java @@ -8,6 +8,7 @@ */ package org.eclipse.hawkbit.ddi.json.model; +import java.util.Collections; import java.util.List; import javax.validation.constraints.NotNull; @@ -57,7 +58,11 @@ public class DdiStatus { } public List getDetails() { - return details; + if (details == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(details); } /** @@ -98,7 +103,7 @@ public class DdiStatus { private String name; - private ExecutionStatus(final String name) { + ExecutionStatus(final String name) { this.name = name; } diff --git a/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/rest/api/DdiRootControllerRestApi.java b/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/rest/api/DdiRootControllerRestApi.java index c33a07d60..432cd7e4a 100644 --- a/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/rest/api/DdiRootControllerRestApi.java +++ b/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/rest/api/DdiRootControllerRestApi.java @@ -39,17 +39,17 @@ public interface DdiRootControllerRestApi { * Returns all artifacts of a given software module and target. * * @param tenant - * of the request - * @param targetid + * of the client + * @param controllerId * of the target that matches to controller id * @param softwareModuleId * of the software module * @return the response */ - @RequestMapping(method = RequestMethod.GET, value = "/{targetid}/softwaremodules/{softwareModuleId}/artifacts", produces = { + @RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/softwaremodules/{softwareModuleId}/artifacts", produces = { "application/hal+json", MediaType.APPLICATION_JSON_VALUE }) - ResponseEntity> getSoftwareModulesArtifacts( - @PathVariable("tenant") final String tenant, @PathVariable("targetid") final String targetid, + ResponseEntity> getSoftwareModulesArtifacts(@PathVariable("tenant") final String tenant, + @PathVariable("controllerId") final String controllerId, @PathVariable("softwareModuleId") final Long softwareModuleId); /** @@ -57,16 +57,16 @@ public interface DdiRootControllerRestApi { * * @param tenant * of the request - * @param targetid + * @param controllerId * of the target that matches to controller id * @param request * the HTTP request injected by spring * @return the response */ - @RequestMapping(method = RequestMethod.GET, value = "/{targetid}", produces = { "application/hal+json", + @RequestMapping(method = RequestMethod.GET, value = "/{controllerId}", produces = { "application/hal+json", MediaType.APPLICATION_JSON_VALUE }) ResponseEntity getControllerBase(@PathVariable("tenant") final String tenant, - @PathVariable("targetid") final String targetid); + @PathVariable("controllerId") final String controllerId); /** * Handles GET {@link DdiArtifact} download request. This could be full or @@ -74,8 +74,8 @@ public interface DdiRootControllerRestApi { * * @param tenant * of the request - * @param targetid - * of the related target + * @param controllerId + * of the target * @param softwareModuleId * of the parent software module * @param fileName @@ -89,9 +89,9 @@ public interface DdiRootControllerRestApi { * {@link HttpStatus#OK} or in case of partial download * {@link HttpStatus#PARTIAL_CONTENT}. */ - @RequestMapping(method = RequestMethod.GET, value = "/{targetid}/softwaremodules/{softwareModuleId}/artifacts/{fileName}") + @RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{fileName}") ResponseEntity downloadArtifact(@PathVariable("tenant") final String tenant, - @PathVariable("targetid") final String targetid, + @PathVariable("controllerId") final String controllerId, @PathVariable("softwareModuleId") final Long softwareModuleId, @PathVariable("fileName") final String fileName); @@ -100,8 +100,8 @@ public interface DdiRootControllerRestApi { * * @param tenant * of the request - * @param targetid - * of the related target + * @param controllerId + * of the target * @param softwareModuleId * of the parent software module * @param fileName @@ -114,10 +114,10 @@ public interface DdiRootControllerRestApi { * @return {@link ResponseEntity} with status {@link HttpStatus#OK} if * successful */ - @RequestMapping(method = RequestMethod.GET, value = "/{targetid}/softwaremodules/{softwareModuleId}/artifacts/{fileName}" + @RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{fileName}" + DdiRestConstants.ARTIFACT_MD5_DWNL_SUFFIX, produces = MediaType.TEXT_PLAIN_VALUE) ResponseEntity downloadArtifactMd5(@PathVariable("tenant") final String tenant, - @PathVariable("targetid") final String targetid, + @PathVariable("controllerId") final String controllerId, @PathVariable("softwareModuleId") final Long softwareModuleId, @PathVariable("fileName") final String fileName); @@ -126,8 +126,8 @@ public interface DdiRootControllerRestApi { * * @param tenant * of the request - * @param targetid - * of the target that matches to controller id + * @param controllerId + * of the target * @param actionId * of the {@link DdiDeploymentBase} that matches to active * actions. @@ -139,10 +139,10 @@ public interface DdiRootControllerRestApi { * the HTTP request injected by spring * @return the response */ - @RequestMapping(value = "/{targetid}/" + DdiRestConstants.DEPLOYMENT_BASE_ACTION + @RequestMapping(value = "/{controllerId}/" + DdiRestConstants.DEPLOYMENT_BASE_ACTION + "/{actionId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) ResponseEntity getControllerBasedeploymentAction(@PathVariable("tenant") final String tenant, - @PathVariable("targetid") @NotEmpty final String targetid, + @PathVariable("controllerId") @NotEmpty final String controllerId, @PathVariable("actionId") @NotEmpty final Long actionId, @RequestParam(value = "c", required = false, defaultValue = "-1") final int resource); @@ -150,10 +150,10 @@ public interface DdiRootControllerRestApi { * This is the feedback channel for the {@link DdiDeploymentBase} action. * * @param tenant - * of the request + * of the client * @param feedback * to provide - * @param targetid + * @param controllerId * of the target that matches to controller id * @param actionId * of the action we have feedback for @@ -162,37 +162,37 @@ public interface DdiRootControllerRestApi { * * @return the response */ - @RequestMapping(value = "/{targetid}/" + DdiRestConstants.DEPLOYMENT_BASE_ACTION + "/{actionId}/" + @RequestMapping(value = "/{controllerId}/" + DdiRestConstants.DEPLOYMENT_BASE_ACTION + "/{actionId}/" + DdiRestConstants.FEEDBACK, method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE) - ResponseEntity postBasedeploymentActionFeedback(@PathVariable("tenant") final String tenant, - @Valid final DdiActionFeedback feedback, @PathVariable("targetid") final String targetid, + ResponseEntity postBasedeploymentActionFeedback(@Valid final DdiActionFeedback feedback, + @PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId, @PathVariable("actionId") @NotEmpty final Long actionId); /** * This is the feedback channel for the config data action. * * @param tenant - * of the request + * of the client * @param configData * as body - * @param targetid + * @param controllerId * to provide data for * @param request * the HTTP request injected by spring * * @return status of the request */ - @RequestMapping(value = "/{targetid}/" + @RequestMapping(value = "/{controllerId}/" + DdiRestConstants.CONFIG_DATA_ACTION, method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE) - ResponseEntity putConfigData(@PathVariable("tenant") final String tenant, - @Valid final DdiConfigData configData, @PathVariable("targetid") final String targetid); + ResponseEntity putConfigData(@Valid final DdiConfigData configData, + @PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId); /** * RequestMethod.GET method for the {@link DdiCancel} action. * * @param tenant * of the request - * @param targetid + * @param controllerId * ID of the calling target * @param actionId * of the action @@ -201,10 +201,10 @@ public interface DdiRootControllerRestApi { * * @return the {@link DdiCancel} response */ - @RequestMapping(value = "/{targetid}/" + DdiRestConstants.CANCEL_ACTION + @RequestMapping(value = "/{controllerId}/" + DdiRestConstants.CANCEL_ACTION + "/{actionId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) ResponseEntity getControllerCancelAction(@PathVariable("tenant") final String tenant, - @PathVariable("targetid") @NotEmpty final String targetid, + @PathVariable("controllerId") @NotEmpty final String controllerId, @PathVariable("actionId") @NotEmpty final Long actionId); /** @@ -212,10 +212,10 @@ public interface DdiRootControllerRestApi { * the target. * * @param tenant - * of the request + * of the client * @param feedback * the {@link DdiActionFeedback} from the target. - * @param targetid + * @param controllerId * the ID of the calling target * @param actionId * of the action we have feedback for @@ -225,10 +225,11 @@ public interface DdiRootControllerRestApi { * @return the {@link DdiActionFeedback} response */ - @RequestMapping(value = "/{targetid}/" + DdiRestConstants.CANCEL_ACTION + "/{actionId}/" + @RequestMapping(value = "/{controllerId}/" + DdiRestConstants.CANCEL_ACTION + "/{actionId}/" + DdiRestConstants.FEEDBACK, method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE) - ResponseEntity postCancelActionFeedback(@PathVariable("tenant") final String tenant, - @Valid final DdiActionFeedback feedback, @PathVariable("targetid") @NotEmpty final String targetid, + ResponseEntity postCancelActionFeedback(@Valid final DdiActionFeedback feedback, + @PathVariable("tenant") final String tenant, + @PathVariable("controllerId") @NotEmpty final String controllerId, @PathVariable("actionId") @NotEmpty final Long actionId); } diff --git a/hawkbit-ddi-dl-api/src/main/java/org/eclipse/hawkbit/ddi/dl/rest/api/DdiDlArtifactStoreControllerRestApi.java b/hawkbit-ddi-dl-api/src/main/java/org/eclipse/hawkbit/ddi/dl/rest/api/DdiDlArtifactStoreControllerRestApi.java index 726f5de57..0d60eb7a2 100644 --- a/hawkbit-ddi-dl-api/src/main/java/org/eclipse/hawkbit/ddi/dl/rest/api/DdiDlArtifactStoreControllerRestApi.java +++ b/hawkbit-ddi-dl-api/src/main/java/org/eclipse/hawkbit/ddi/dl/rest/api/DdiDlArtifactStoreControllerRestApi.java @@ -12,7 +12,7 @@ import java.io.InputStream; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.security.web.bind.annotation.AuthenticationPrincipal; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @@ -27,7 +27,9 @@ public interface DdiDlArtifactStoreControllerRestApi { /** * Handles GET download request. This could be full or partial download * request. - * + * + * @param tenant + * name of the client * @param fileName * to search for * @param targetid @@ -40,12 +42,14 @@ public interface DdiDlArtifactStoreControllerRestApi { @RequestMapping(method = RequestMethod.GET, value = DdiDlRestConstants.ARTIFACT_DOWNLOAD_BY_FILENAME + "/{fileName}") @ResponseBody - public ResponseEntity downloadArtifactByFilename(@PathVariable("tenant") final String tenant, + ResponseEntity downloadArtifactByFilename(@PathVariable("tenant") final String tenant, @PathVariable("fileName") final String fileName, @AuthenticationPrincipal final String targetid); /** * Handles GET MD5 checksum file download request. * + * @param tenant + * name of the client * @param fileName * to search for * @@ -54,7 +58,7 @@ public interface DdiDlArtifactStoreControllerRestApi { @RequestMapping(method = RequestMethod.GET, value = DdiDlRestConstants.ARTIFACT_DOWNLOAD_BY_FILENAME + "/{fileName}" + DdiDlRestConstants.ARTIFACT_MD5_DWNL_SUFFIX) @ResponseBody - public ResponseEntity downloadArtifactMD5ByFilename(@PathVariable("tenant") final String tenant, + ResponseEntity downloadArtifactMD5ByFilename(@PathVariable("tenant") final String tenant, @PathVariable("fileName") final String fileName); } diff --git a/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java b/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java index 3a50ede61..a09155739 100644 --- a/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java +++ b/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java @@ -18,8 +18,10 @@ import java.util.stream.Collectors; import javax.servlet.http.HttpServletResponse; +import org.eclipse.hawkbit.api.ApiType; import org.eclipse.hawkbit.api.ArtifactUrlHandler; -import org.eclipse.hawkbit.api.UrlProtocol; +import org.eclipse.hawkbit.api.URLPlaceholder; +import org.eclipse.hawkbit.api.URLPlaceholder.SoftwareData; import org.eclipse.hawkbit.ddi.dl.rest.api.DdiDlRestConstants; import org.eclipse.hawkbit.ddi.json.model.DdiArtifact; import org.eclipse.hawkbit.ddi.json.model.DdiArtifactHash; @@ -28,6 +30,7 @@ import org.eclipse.hawkbit.ddi.json.model.DdiConfig; import org.eclipse.hawkbit.ddi.json.model.DdiControllerBase; import org.eclipse.hawkbit.ddi.json.model.DdiPolling; import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants; +import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.LocalArtifact; import org.eclipse.hawkbit.repository.model.Target; @@ -45,11 +48,11 @@ public final class DataConversionHelper { } - static List createChunks(final String targetid, final Action uAction, - final ArtifactUrlHandler artifactUrlHandler) { + static List createChunks(final Target target, final Action uAction, + final ArtifactUrlHandler artifactUrlHandler, final SystemManagement systemManagement) { return uAction.getDistributionSet().getModules().stream() .map(module -> new DdiChunk(mapChunkLegacyKeys(module.getType().getKey()), module.getVersion(), - module.getName(), createArtifacts(targetid, module, artifactUrlHandler))) + module.getName(), createArtifacts(target, module, artifactUrlHandler, systemManagement))) .collect(Collectors.toList()); } @@ -68,43 +71,42 @@ public final class DataConversionHelper { /** * Creates all (rest) artifacts for a given software module. * - * @param targetid - * of the target + * @param target + * for create URLs for * @param module * the software module * @param artifactUrlHandler * for creating download URLs + * @param systemManagement + * for access to tenant meta data * @return a list of artifacts or a empty list. Cannot be . */ - public static List createArtifacts(final String targetid, + public static List createArtifacts(final Target target, final org.eclipse.hawkbit.repository.model.SoftwareModule module, - final ArtifactUrlHandler artifactUrlHandler) { + final ArtifactUrlHandler artifactUrlHandler, final SystemManagement systemManagement) { return module.getLocalArtifacts().stream() - .map(artifact -> createArtifact(targetid, artifactUrlHandler, artifact)).collect(Collectors.toList()); + .map(artifact -> createArtifact(target, artifactUrlHandler, artifact, systemManagement)) + .collect(Collectors.toList()); } - private static DdiArtifact createArtifact(final String targetid, final ArtifactUrlHandler artifactUrlHandler, - final LocalArtifact artifact) { + private static DdiArtifact createArtifact(final Target target, final ArtifactUrlHandler artifactUrlHandler, + final LocalArtifact artifact, final SystemManagement systemManagement) { final DdiArtifact file = new DdiArtifact(); file.setHashes(new DdiArtifactHash(artifact.getSha1Hash(), artifact.getMd5Hash())); file.setFilename(artifact.getFilename()); file.setSize(artifact.getSize()); - if (artifactUrlHandler.protocolSupported(UrlProtocol.HTTP)) { - final String linkHttp = artifactUrlHandler.getUrl(targetid, artifact.getSoftwareModule().getId(), - artifact.getFilename(), artifact.getSha1Hash(), UrlProtocol.HTTP); - file.add(new Link(linkHttp).withRel("download-http")); - file.add(new Link(linkHttp + DdiDlRestConstants.ARTIFACT_MD5_DWNL_SUFFIX).withRel("md5sum-http")); - } + artifactUrlHandler + .getUrls(new URLPlaceholder(systemManagement.getTenantMetadata().getTenant(), + systemManagement.getTenantMetadata().getId(), target.getControllerId(), target.getId(), + new SoftwareData(artifact.getSoftwareModule().getId(), artifact.getFilename(), artifact.getId(), + artifact.getSha1Hash())), + ApiType.DDI) + .forEach(entry -> file.add(new Link(entry.getRef()).withRel(entry.getRel()))); - if (artifactUrlHandler.protocolSupported(UrlProtocol.HTTPS)) { - final String linkHttps = artifactUrlHandler.getUrl(targetid, artifact.getSoftwareModule().getId(), - artifact.getFilename(), artifact.getSha1Hash(), UrlProtocol.HTTPS); - file.add(new Link(linkHttps).withRel("download")); - file.add(new Link(linkHttps + DdiDlRestConstants.ARTIFACT_MD5_DWNL_SUFFIX).withRel("md5sum")); - } return file; + } static DdiControllerBase fromTarget(final Target target, final Optional action, @@ -132,8 +134,8 @@ public final class DataConversionHelper { } if (target.getTargetInfo().isRequestControllerAttributes()) { - result.add(linkTo(methodOn(DdiRootController.class, tenantAware.getCurrentTenant()) - .putConfigData(tenantAware.getCurrentTenant(), null, target.getControllerId())) + result.add(linkTo(methodOn(DdiRootController.class, tenantAware.getCurrentTenant()).putConfigData(null, + tenantAware.getCurrentTenant(), target.getControllerId())) .withRel(DdiRestConstants.CONFIG_DATA_ACTION)); } return result; diff --git a/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootController.java b/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootController.java index 5e5c0d7c5..7506ddb17 100644 --- a/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootController.java +++ b/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootController.java @@ -33,6 +33,7 @@ import org.eclipse.hawkbit.repository.ControllerManagement; import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.RepositoryConstants; import org.eclipse.hawkbit.repository.SoftwareManagement; +import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action.Status; @@ -88,6 +89,9 @@ public class DdiRootController implements DdiRootControllerRestApi { @Autowired private TenantAware tenantAware; + @Autowired + private SystemManagement systemManagement; + @Autowired private ArtifactUrlHandler artifactUrlHandler; @@ -99,9 +103,12 @@ public class DdiRootController implements DdiRootControllerRestApi { @Override public ResponseEntity> getSoftwareModulesArtifacts( - @PathVariable("tenant") final String tenant, @PathVariable("targetid") final String targetid, + @PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId, @PathVariable("softwareModuleId") final Long softwareModuleId) { - LOG.debug("getSoftwareModulesArtifacts({})", targetid); + LOG.debug("getSoftwareModulesArtifacts({})", controllerId); + + final Target target = controllerManagement.updateLastTargetQuery(controllerId, IpUtil + .getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties)); final SoftwareModule softwareModule = softwareManagement.findSoftwareModuleById(softwareModuleId); @@ -111,20 +118,21 @@ public class DdiRootController implements DdiRootControllerRestApi { } - return new ResponseEntity<>(DataConversionHelper.createArtifacts(targetid, softwareModule, artifactUrlHandler), + return new ResponseEntity<>( + DataConversionHelper.createArtifacts(target, softwareModule, artifactUrlHandler, systemManagement), HttpStatus.OK); } @Override public ResponseEntity getControllerBase(@PathVariable("tenant") final String tenant, - @PathVariable("targetid") final String targetid) { - LOG.debug("getControllerBase({})", targetid); + @PathVariable("controllerId") final String controllerId) { + LOG.debug("getControllerBase({})", controllerId); - final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotexist(targetid, IpUtil + final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotexist(controllerId, IpUtil .getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties)); if (target.getTargetInfo().getUpdateStatus() == TargetUpdateStatus.UNKNOWN) { - LOG.debug("target with {} extsisted but was in status UNKNOWN -> REGISTERED)", targetid); + LOG.debug("target with {} extsisted but was in status UNKNOWN -> REGISTERED)", controllerId); controllerManagement.updateTargetStatus(target.getTargetInfo(), TargetUpdateStatus.REGISTERED, System.currentTimeMillis(), IpUtil.getClientIpFromRequest( requestResponseContextHolder.getHttpServletRequest(), securityProperties)); @@ -138,12 +146,12 @@ public class DdiRootController implements DdiRootControllerRestApi { @Override public ResponseEntity downloadArtifact(@PathVariable("tenant") final String tenant, - @PathVariable("targetid") final String targetid, + @PathVariable("controllerId") final String controllerId, @PathVariable("softwareModuleId") final Long softwareModuleId, @PathVariable("fileName") final String fileName) { ResponseEntity result; - final Target target = controllerManagement.updateLastTargetQuery(targetid, IpUtil + final Target target = controllerManagement.updateLastTargetQuery(controllerId, IpUtil .getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties)); final SoftwareModule module = softwareManagement.findSoftwareModuleById(softwareModuleId); @@ -205,10 +213,10 @@ public class DdiRootController implements DdiRootControllerRestApi { // subroutine @SuppressWarnings("squid:S3655") public ResponseEntity downloadArtifactMd5(@PathVariable("tenant") final String tenant, - @PathVariable("targetid") final String targetid, + @PathVariable("controllerId") final String controllerId, @PathVariable("softwareModuleId") final Long softwareModuleId, @PathVariable("fileName") final String fileName) { - controllerManagement.updateLastTargetQuery(targetid, IpUtil + controllerManagement.updateLastTargetQuery(controllerId, IpUtil .getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties)); final SoftwareModule module = softwareManagement.findSoftwareModuleById(softwareModuleId); @@ -231,12 +239,12 @@ public class DdiRootController implements DdiRootControllerRestApi { @Override public ResponseEntity getControllerBasedeploymentAction( - @PathVariable("tenant") final String tenant, @PathVariable("targetid") final String targetid, + @PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId, @PathVariable("actionId") final Long actionId, @RequestParam(value = "c", required = false, defaultValue = "-1") final int resource) { - LOG.debug("getControllerBasedeploymentAction({},{})", targetid, resource); + LOG.debug("getControllerBasedeploymentAction({},{})", controllerId, resource); - final Target target = controllerManagement.updateLastTargetQuery(targetid, IpUtil + final Target target = controllerManagement.updateLastTargetQuery(controllerId, IpUtil .getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties)); final Action action = findActionWithExceptionIfNotFound(actionId); @@ -247,14 +255,15 @@ public class DdiRootController implements DdiRootControllerRestApi { if (!action.isCancelingOrCanceled()) { - final List chunks = DataConversionHelper.createChunks(targetid, action, artifactUrlHandler); + final List chunks = DataConversionHelper.createChunks(target, action, artifactUrlHandler, + systemManagement); final HandlingType handlingType = action.isForce() ? HandlingType.FORCED : HandlingType.ATTEMPT; final DdiDeploymentBase base = new DdiDeploymentBase(Long.toString(action.getId()), new DdiDeployment(handlingType, handlingType, chunks)); - LOG.debug("Found an active UpdateAction for target {}. returning deyploment: {}", targetid, base); + LOG.debug("Found an active UpdateAction for target {}. returning deyploment: {}", controllerId, base); controllerManagement.registerRetrieved(action, RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target retrieved update action and should start now the download."); @@ -266,12 +275,12 @@ public class DdiRootController implements DdiRootControllerRestApi { } @Override - public ResponseEntity postBasedeploymentActionFeedback(@PathVariable("tenant") final String tenant, - @Valid @RequestBody final DdiActionFeedback feedback, @PathVariable("targetid") final String targetid, + public ResponseEntity postBasedeploymentActionFeedback(@Valid @RequestBody final DdiActionFeedback feedback, + @PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId, @PathVariable("actionId") @NotEmpty final Long actionId) { - LOG.debug("provideBasedeploymentActionFeedback for target [{},{}]: {}", targetid, actionId, feedback); + LOG.debug("provideBasedeploymentActionFeedback for target [{},{}]: {}", controllerId, actionId, feedback); - final Target target = controllerManagement.updateLastTargetQuery(targetid, IpUtil + final Target target = controllerManagement.updateLastTargetQuery(controllerId, IpUtil .getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties)); if (!actionId.equals(feedback.getId())) { @@ -293,13 +302,14 @@ public class DdiRootController implements DdiRootControllerRestApi { return new ResponseEntity<>(HttpStatus.GONE); } - controllerManagement.addUpdateActionStatus(generateUpdateStatus(feedback, targetid, feedback.getId(), action)); + controllerManagement + .addUpdateActionStatus(generateUpdateStatus(feedback, controllerId, feedback.getId(), action)); return new ResponseEntity<>(HttpStatus.OK); } - private ActionStatus generateUpdateStatus(final DdiActionFeedback feedback, final String targetid, + private ActionStatus generateUpdateStatus(final DdiActionFeedback feedback, final String controllerId, final Long actionid, final Action action) { final ActionStatus actionStatus = entityFactory.generateActionStatus(); @@ -308,22 +318,22 @@ public class DdiRootController implements DdiRootControllerRestApi { switch (feedback.getStatus().getExecution()) { case CANCELED: - LOG.debug("Controller confirmed cancel (actionid: {}, targetid: {}) as we got {} report.", actionid, - targetid, feedback.getStatus().getExecution()); + LOG.debug("Controller confirmed cancel (actionid: {}, controllerId: {}) as we got {} report.", actionid, + controllerId, feedback.getStatus().getExecution()); actionStatus.setStatus(Status.CANCELED); actionStatus.addMessage(RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target confirmed cancelation."); break; case REJECTED: - LOG.info("Controller reported internal error (actionid: {}, targetid: {}) as we got {} report.", actionid, - targetid, feedback.getStatus().getExecution()); + LOG.info("Controller reported internal error (actionid: {}, controllerId: {}) as we got {} report.", + actionid, controllerId, feedback.getStatus().getExecution()); actionStatus.setStatus(Status.WARNING); actionStatus.addMessage(RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target REJECTED update."); break; case CLOSED: - handleClosedUpdateStatus(feedback, targetid, actionid, actionStatus); + handleClosedUpdateStatus(feedback, controllerId, actionid, actionStatus); break; default: - handleDefaultUpdateStatus(feedback, targetid, actionid, actionStatus); + handleDefaultUpdateStatus(feedback, controllerId, actionid, actionStatus); break; } @@ -339,19 +349,19 @@ public class DdiRootController implements DdiRootControllerRestApi { return actionStatus; } - private static void handleDefaultUpdateStatus(final DdiActionFeedback feedback, final String targetid, + private static void handleDefaultUpdateStatus(final DdiActionFeedback feedback, final String controllerId, final Long actionid, final ActionStatus actionStatus) { - LOG.debug("Controller reported intermediate status (actionid: {}, targetid: {}) as we got {} report.", actionid, - targetid, feedback.getStatus().getExecution()); + LOG.debug("Controller reported intermediate status (actionid: {}, controllerId: {}) as we got {} report.", + actionid, controllerId, feedback.getStatus().getExecution()); actionStatus.setStatus(Status.RUNNING); actionStatus.addMessage( RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target reported " + feedback.getStatus().getExecution()); } - private static void handleClosedUpdateStatus(final DdiActionFeedback feedback, final String targetid, + private static void handleClosedUpdateStatus(final DdiActionFeedback feedback, final String controllerId, final Long actionid, final ActionStatus actionStatus) { - LOG.debug("Controller reported closed (actionid: {}, targetid: {}) as we got {} report.", actionid, targetid, - feedback.getStatus().getExecution()); + LOG.debug("Controller reported closed (actionid: {}, controllerId: {}) as we got {} report.", actionid, + controllerId, feedback.getStatus().getExecution()); if (feedback.getStatus().getResult().getFinished() == FinalResult.FAILURE) { actionStatus.setStatus(Status.ERROR); actionStatus.addMessage(RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target reported CLOSED with ERROR!"); @@ -362,23 +372,23 @@ public class DdiRootController implements DdiRootControllerRestApi { } @Override - public ResponseEntity putConfigData(@PathVariable("tenant") final String tenant, - @Valid @RequestBody final DdiConfigData configData, @PathVariable("targetid") final String targetid) { - controllerManagement.updateLastTargetQuery(targetid, IpUtil + public ResponseEntity putConfigData(@Valid @RequestBody final DdiConfigData configData, + @PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId) { + controllerManagement.updateLastTargetQuery(controllerId, IpUtil .getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties)); - controllerManagement.updateControllerAttributes(targetid, configData.getData()); + controllerManagement.updateControllerAttributes(controllerId, configData.getData()); return new ResponseEntity<>(HttpStatus.OK); } @Override public ResponseEntity getControllerCancelAction(@PathVariable("tenant") final String tenant, - @PathVariable("targetid") @NotEmpty final String targetid, + @PathVariable("controllerId") @NotEmpty final String controllerId, @PathVariable("actionId") @NotEmpty final Long actionId) { - LOG.debug("getControllerCancelAction({})", targetid); + LOG.debug("getControllerCancelAction({})", controllerId); - final Target target = controllerManagement.updateLastTargetQuery(targetid, IpUtil + final Target target = controllerManagement.updateLastTargetQuery(controllerId, IpUtil .getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties)); final Action action = findActionWithExceptionIfNotFound(actionId); @@ -391,7 +401,7 @@ public class DdiRootController implements DdiRootControllerRestApi { final DdiCancel cancel = new DdiCancel(String.valueOf(action.getId()), new DdiCancelActionToStop(String.valueOf(action.getId()))); - LOG.debug("Found an active CancelAction for target {}. returning cancel: {}", targetid, cancel); + LOG.debug("Found an active CancelAction for target {}. returning cancel: {}", controllerId, cancel); controllerManagement.registerRetrieved(action, RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target retrieved cancel action and should start now the cancelation."); @@ -403,13 +413,13 @@ public class DdiRootController implements DdiRootControllerRestApi { } @Override - public ResponseEntity postCancelActionFeedback(@PathVariable("tenant") final String tenant, - @Valid @RequestBody final DdiActionFeedback feedback, - @PathVariable("targetid") @NotEmpty final String targetid, + public ResponseEntity postCancelActionFeedback(@Valid @RequestBody final DdiActionFeedback feedback, + @PathVariable("tenant") final String tenant, + @PathVariable("controllerId") @NotEmpty final String controllerId, @PathVariable("actionId") @NotEmpty final Long actionId) { - LOG.debug("provideCancelActionFeedback for target [{}]: {}", targetid, feedback); + LOG.debug("provideCancelActionFeedback for target [{}]: {}", controllerId, feedback); - final Target target = controllerManagement.updateLastTargetQuery(targetid, IpUtil + final Target target = controllerManagement.updateLastTargetQuery(controllerId, IpUtil .getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties)); if (!actionId.equals(feedback.getId())) { @@ -440,13 +450,13 @@ public class DdiRootController implements DdiRootControllerRestApi { switch (feedback.getStatus().getExecution()) { case CANCELED: LOG.error( - "Controller reported cancel for a cancel which is not supported by the server (actionid: {}, targetid: {}) as we got {} report.", + "Controller reported cancel for a cancel which is not supported by the server (actionid: {}, controllerId: {}) as we got {} report.", actionid, target.getControllerId(), feedback.getStatus().getExecution()); actionStatus.setStatus(Status.WARNING); break; case REJECTED: - LOG.info("Controller rejected the cancelation request (too late) (actionid: {}, targetid: {}).", actionid, - target.getControllerId()); + LOG.info("Controller rejected the cancelation request (too late) (actionid: {}, controllerId: {}).", + actionid, target.getControllerId()); actionStatus.setStatus(Status.WARNING); break; case CLOSED: diff --git a/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java b/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java index f9625dff7..f97d57bc3 100644 --- a/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java +++ b/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java @@ -61,8 +61,7 @@ import ru.yandex.qatools.allure.annotations.Stories; @Stories("Deployment Action Resource") public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoDB { - private static final String HTTP_LOCALHOST = "http://localhost/"; - private static final String HTTPS_LOCALHOST = "https://localhost/"; + private static final String HTTP_LOCALHOST = "http://localhost:8080/"; @Test() @Description("Ensures that artifacts are not found, when softare module does not exists.") @@ -174,25 +173,14 @@ public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoD .andExpect( jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0].hashes.sha1", contains(artifact.getSha1Hash()))) - .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.download.href", - contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant() - + "/controller/v1/4712/softwaremodules/" - + findDistributionSetByAction.findFirstModuleByType(osType).getId() - + "/artifacts/test1"))) - .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.md5sum.href", - contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant() - + "/controller/v1/4712/softwaremodules/" - + findDistributionSetByAction.findFirstModuleByType(osType).getId() - + "/artifacts/test1.MD5SUM"))) - .andExpect( - jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.download-http.href", + jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.download.href", contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/" + findDistributionSetByAction.findFirstModuleByType(osType).getId() + "/artifacts/test1"))) .andExpect( - jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.md5sum-http.href", + jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.md5sum.href", contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/" + findDistributionSetByAction.findFirstModuleByType(osType).getId() @@ -203,27 +191,17 @@ public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoD contains("test1.signature"))) .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].hashes.md5", contains(artifactSignature.getMd5Hash()))) + .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].hashes.sha1", + contains(artifactSignature.getSha1Hash()))) + .andExpect( - jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].hashes.sha1", - contains(artifactSignature.getSha1Hash()))) - .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download.href", - contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant() - + "/controller/v1/4712/softwaremodules/" - + findDistributionSetByAction.findFirstModuleByType(osType).getId() - + "/artifacts/test1.signature"))) - .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum.href", - contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant() - + "/controller/v1/4712/softwaremodules/" - + findDistributionSetByAction.findFirstModuleByType(osType).getId() - + "/artifacts/test1.signature.MD5SUM"))) - .andExpect( - jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download-http.href", + jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download.href", contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/" + findDistributionSetByAction.findFirstModuleByType(osType).getId() + "/artifacts/test1.signature"))) .andExpect( - jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum-http.href", + jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum.href", contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/" + findDistributionSetByAction.findFirstModuleByType(osType).getId() @@ -359,16 +337,18 @@ public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoD .andExpect( jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0].hashes.sha1", contains(artifact.getSha1Hash()))) - .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.download.href", - contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant() - + "/controller/v1/4712/softwaremodules/" - + findDistributionSetByAction.findFirstModuleByType(osType).getId() - + "/artifacts/test1"))) - .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.md5sum.href", - contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant() - + "/controller/v1/4712/softwaremodules/" - + findDistributionSetByAction.findFirstModuleByType(osType).getId() - + "/artifacts/test1.MD5SUM"))) + .andExpect( + jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.download.href", + contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + + "/controller/v1/4712/softwaremodules/" + + findDistributionSetByAction.findFirstModuleByType(osType).getId() + + "/artifacts/test1"))) + .andExpect( + jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.md5sum.href", + contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + + "/controller/v1/4712/softwaremodules/" + + findDistributionSetByAction.findFirstModuleByType(osType).getId() + + "/artifacts/test1.MD5SUM"))) .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].size", contains(5 * 1024))) .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].filename", contains("test1.signature"))) @@ -377,24 +357,14 @@ public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoD .andExpect( jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].hashes.sha1", contains(artifactSignature.getSha1Hash()))) - .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download.href", - contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant() - + "/controller/v1/4712/softwaremodules/" - + findDistributionSetByAction.findFirstModuleByType(osType).getId() - + "/artifacts/test1.signature"))) - .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum.href", - contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant() - + "/controller/v1/4712/softwaremodules/" - + findDistributionSetByAction.findFirstModuleByType(osType).getId() - + "/artifacts/test1.signature.MD5SUM"))) .andExpect( - jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download-http.href", + jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download.href", contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/" + findDistributionSetByAction.findFirstModuleByType(osType).getId() + "/artifacts/test1.signature"))) .andExpect( - jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum-http.href", + jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum.href", contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/" + findDistributionSetByAction.findFirstModuleByType(osType).getId() @@ -486,31 +456,22 @@ public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoD .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0].filename", contains("test1"))) .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0].hashes.md5", contains(artifact.getMd5Hash()))) - .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0].hashes.sha1", - contains(artifact.getSha1Hash()))) - - .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.download.href", - contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant() - + "/controller/v1/4712/softwaremodules/" - + findDistributionSetByAction.findFirstModuleByType(osType).getId() - + "/artifacts/test1"))) - .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.md5sum.href", - contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant() - + "/controller/v1/4712/softwaremodules/" - + findDistributionSetByAction.findFirstModuleByType(osType).getId() - + "/artifacts/test1.MD5SUM"))) .andExpect( - jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.download-http.href", + jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0].hashes.sha1", + contains(artifact.getSha1Hash()))) + .andExpect( + jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.download.href", contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/" + findDistributionSetByAction.findFirstModuleByType(osType).getId() + "/artifacts/test1"))) .andExpect( - jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.md5sum-http.href", + jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.md5sum.href", contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/" + findDistributionSetByAction.findFirstModuleByType(osType).getId() + "/artifacts/test1.MD5SUM"))) + .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].size", contains(5 * 1024))) .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].filename", contains("test1.signature"))) @@ -519,25 +480,14 @@ public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoD .andExpect( jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].hashes.sha1", contains(artifactSignature.getSha1Hash()))) - .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download.href", - contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant() - + "/controller/v1/4712/softwaremodules/" - + findDistributionSetByAction.findFirstModuleByType(osType).getId() - + "/artifacts/test1.signature"))) - .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum.href", - contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant() - + "/controller/v1/4712/softwaremodules/" - + findDistributionSetByAction.findFirstModuleByType(osType).getId() - + "/artifacts/test1.signature.MD5SUM"))) - .andExpect( - jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download-http.href", + jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download.href", contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/" + findDistributionSetByAction.findFirstModuleByType(osType).getId() + "/artifacts/test1.signature"))) .andExpect( - jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum-http.href", + jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum.href", contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/" + findDistributionSetByAction.findFirstModuleByType(osType).getId() diff --git a/hawkbit-ddi-resource/src/test/resources/application-test.properties b/hawkbit-ddi-resource/src/test/resources/application-test.properties new file mode 100644 index 000000000..599441734 --- /dev/null +++ b/hawkbit-ddi-resource/src/test/resources/application-test.properties @@ -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 +# + +spring.data.mongodb.uri=mongodb://localhost/spArtifactRepository${random.value} +spring.data.mongodb.port=28017 + +hawkbit.server.ddi.security.authentication.header.enabled=true +hawkbit.server.ddi.security.authentication.gatewaytoken.name=TestToken + +multipart.max-file-size=5MB + +hawkbit.server.security.dos.maxStatusEntriesPerAction=100 + +hawkbit.server.security.dos.maxAttributeEntriesPerTarget=10 + +spring.jpa.database=H2 +spring.datasource.url=jdbc:h2:mem:sp-db;DB_CLOSE_ON_EXIT=FALSE +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password=sa + +flyway.enabled=true +flyway.sqlMigrationSuffix=${spring.jpa.database}.sql +#spring.jpa.show-sql=true + +# DDI configuration +hawkbit.controller.pollingTime=00:01:00 +hawkbit.controller.pollingOverdueTime=00:01:00 + +hawkbit.artifact.url.protocols[0].rel=download +hawkbit.artifact.url.protocols[0].protocol=http +hawkbit.artifact.url.protocols[0].supports=DMF,DDI +hawkbit.artifact.url.protocols[0].ref={protocol}://{hostname}:{port}/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName} +hawkbit.artifact.url.protocols[1].rel=md5sum +hawkbit.artifact.url.protocols[1].protocol=${hawkbit.artifact.url.protocols[0].protocol} +hawkbit.artifact.url.protocols[1].supports=${hawkbit.artifact.url.protocols[0].supports} +hawkbit.artifact.url.protocols[1].ref=${hawkbit.artifact.url.protocols[0].ref}.MD5SUM \ No newline at end of file diff --git a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpAuthenticationMessageHandler.java b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpAuthenticationMessageHandler.java new file mode 100644 index 000000000..fc73116f7 --- /dev/null +++ b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpAuthenticationMessageHandler.java @@ -0,0 +1,236 @@ +/** + * 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.amqp; + +import java.net.URISyntaxException; +import java.util.UUID; + +import org.eclipse.hawkbit.api.HostnameResolver; +import org.eclipse.hawkbit.artifact.repository.model.DbArtifact; +import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash; +import org.eclipse.hawkbit.cache.DownloadArtifactCache; +import org.eclipse.hawkbit.cache.DownloadType; +import org.eclipse.hawkbit.dmf.json.model.Artifact; +import org.eclipse.hawkbit.dmf.json.model.ArtifactHash; +import org.eclipse.hawkbit.dmf.json.model.DownloadResponse; +import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken; +import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken.FileResource; +import org.eclipse.hawkbit.repository.ArtifactManagement; +import org.eclipse.hawkbit.repository.ControllerManagement; +import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; +import org.eclipse.hawkbit.repository.model.LocalArtifact; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.amqp.AmqpRejectAndDontRequeueException; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.core.MessageProperties; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.cache.Cache; +import org.springframework.http.HttpStatus; +import org.springframework.security.authentication.AuthenticationServiceException; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.CredentialsExpiredException; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.util.UriComponentsBuilder; + +/** + * + * {@link AmqpMessageHandlerService} handles all incoming target authentication + * AMQP messages that can be used by 3rd party CDN services to check if a target + * is permitted to download certain artifact. This is handled by the queue that + * is configured for the property + * hawkbit.dmf.rabbitmq.authenticationReceiverQueue. + * + */ +public class AmqpAuthenticationMessageHandler extends BaseAmqpService { + private static final Logger LOG = LoggerFactory.getLogger(AmqpAuthenticationMessageHandler.class); + + private final AmqpControllerAuthentication authenticationManager; + + private final ArtifactManagement artifactManagement; + + private final Cache cache; + + private final HostnameResolver hostnameResolver; + + private final ControllerManagement controllerManagement; + + /** + * @param rabbitTemplate + * the configured amqp template. + * @param artifactManagement + * for artifact URI generation + * @param cache + * for download Ids + * @param hostnameResolver + * for resolving the host for downloads + * @param authenticationManager + * for target authentication + * @param controllerManagement + * for target repo access + */ + public AmqpAuthenticationMessageHandler(final RabbitTemplate rabbitTemplate, + final AmqpControllerAuthentication authenticationManager, final ArtifactManagement artifactManagement, + final Cache cache, final HostnameResolver hostnameResolver, + final ControllerManagement controllerManagement) { + super(rabbitTemplate); + this.authenticationManager = authenticationManager; + this.artifactManagement = artifactManagement; + this.cache = cache; + this.hostnameResolver = hostnameResolver; + this.controllerManagement = controllerManagement; + } + + /** + * Executed on a authentication request. + * + * @param message + * the amqp message + * @return the rpc message back to supplier. + */ + @RabbitListener(queues = "${hawkbit.dmf.rabbitmq.authenticationReceiverQueue}", containerFactory = "listenerContainerFactory") + public Message onAuthenticationRequest(final Message message) { + checkContentTypeJson(message); + final SecurityContext oldContext = SecurityContextHolder.getContext(); + try { + return handleAuthenticationMessage(message); + } catch (final RuntimeException ex) { + throw new AmqpRejectAndDontRequeueException(ex); + } finally { + SecurityContextHolder.setContext(oldContext); + } + } + + /** + * check action for this download purposes, the method will throw an + * EntityNotFoundException in case the controller is not allowed to download + * this file because it's not assigned to an action and not assigned to this + * controller. Otherwise no controllerId is set = anonymous download + * + * @param secruityToken + * the security token which holds the target ID to check on + * @param localArtifact + * the local artifact to verify if the given target is allowed to + * download this artifact + */ + private void checkIfArtifactIsAssignedToTarget(final TenantSecurityToken secruityToken, + final LocalArtifact localArtifact) { + + if (secruityToken.getControllerId() != null) { + checkByControllerId(localArtifact, secruityToken.getControllerId()); + } else if (secruityToken.getTargetId() != null) { + checkByTargetId(localArtifact, secruityToken.getTargetId()); + } else { + LOG.info("anonymous download no authentication check for artifact {}", localArtifact); + return; + } + + } + + private void checkByTargetId(final LocalArtifact localArtifact, final Long targetId) { + LOG.debug("no anonymous download request, doing authentication check for target {} and artifact {}", targetId, + localArtifact); + if (!controllerManagement.hasTargetArtifactAssigned(targetId, localArtifact)) { + LOG.info("target {} tried to download artifact {} which is not assigned to the target", targetId, + localArtifact); + throw new EntityNotFoundException(); + } + LOG.info("download security check for target {} and artifact {} granted", targetId, localArtifact); + } + + private void checkByControllerId(final LocalArtifact localArtifact, final String controllerId) { + LOG.debug("no anonymous download request, doing authentication check for target {} and artifact {}", + controllerId, localArtifact); + if (!controllerManagement.hasTargetArtifactAssigned(controllerId, localArtifact)) { + LOG.info("target {} tried to download artifact {} which is not assigned to the target", controllerId, + localArtifact); + throw new EntityNotFoundException(); + } + LOG.info("download security check for target {} and artifact {} granted", controllerId, localArtifact); + } + + private LocalArtifact findLocalArtifactByFileResource(final FileResource fileResource) { + if (fileResource.getSha1() != null) { + return artifactManagement.findFirstLocalArtifactsBySHA1(fileResource.getSha1()); + } else if (fileResource.getFilename() != null) { + return artifactManagement.findLocalArtifactByFilename(fileResource.getFilename()).stream().findFirst() + .orElse(null); + } else if (fileResource.getArtifactId() != null) { + return artifactManagement.findLocalArtifact(fileResource.getArtifactId()); + } else if (fileResource.getSoftwareModuleFilenameResource() != null) { + return artifactManagement + .findByFilenameAndSoftwareModule(fileResource.getSoftwareModuleFilenameResource().getFilename(), + fileResource.getSoftwareModuleFilenameResource().getSoftwareModuleId()) + .stream().findFirst().orElse(null); + } + return null; + } + + private static Artifact convertDbArtifact(final DbArtifact dbArtifact) { + final Artifact artifact = new Artifact(); + artifact.setSize(dbArtifact.getSize()); + final DbArtifactHash dbArtifactHash = dbArtifact.getHashes(); + artifact.setHashes(new ArtifactHash(dbArtifactHash.getSha1(), dbArtifactHash.getMd5())); + return artifact; + } + + private Message handleAuthenticationMessage(final Message message) { + final DownloadResponse authentificationResponse = new DownloadResponse(); + final MessageProperties messageProperties = message.getMessageProperties(); + final TenantSecurityToken secruityToken = convertMessage(message, TenantSecurityToken.class); + + final FileResource fileResource = secruityToken.getFileResource(); + try { + SecurityContextHolder.getContext().setAuthentication(authenticationManager.doAuthenticate(secruityToken)); + + final LocalArtifact localArtifact = findLocalArtifactByFileResource(fileResource); + + if (localArtifact == null) { + LOG.info("target {} requested file resource {} which does not exists to download", + secruityToken.getControllerId(), fileResource); + throw new EntityNotFoundException(); + } + + checkIfArtifactIsAssignedToTarget(secruityToken, localArtifact); + + final Artifact artifact = convertDbArtifact(artifactManagement.loadLocalArtifactBinary(localArtifact)); + if (artifact == null) { + throw new EntityNotFoundException(); + } + authentificationResponse.setArtifact(artifact); + final String downloadId = UUID.randomUUID().toString(); + // SHA1 key is set, download by SHA1 + final DownloadArtifactCache downloadCache = new DownloadArtifactCache(DownloadType.BY_SHA1, + localArtifact.getSha1Hash()); + cache.put(downloadId, downloadCache); + authentificationResponse + .setDownloadUrl(UriComponentsBuilder.fromUri(hostnameResolver.resolveHostname().toURI()) + .path("/api/v1/downloadserver/downloadId/").path(downloadId).build().toUriString()); + authentificationResponse.setResponseCode(HttpStatus.OK.value()); + } catch (final BadCredentialsException | AuthenticationServiceException | CredentialsExpiredException e) { + LOG.error("Login failed", e); + authentificationResponse.setResponseCode(HttpStatus.FORBIDDEN.value()); + authentificationResponse.setMessage("Login failed"); + } catch (final URISyntaxException e) { + LOG.error("URI build exception", e); + authentificationResponse.setResponseCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); + authentificationResponse.setMessage("Building download URI failed"); + } catch (final EntityNotFoundException e) { + final String errorMessage = "Artifact for resource " + fileResource + "not found "; + LOG.warn(errorMessage, e); + authentificationResponse.setResponseCode(HttpStatus.NOT_FOUND.value()); + authentificationResponse.setMessage(errorMessage); + } + + return getMessageConverter().toMessage(authentificationResponse, messageProperties); + } + +} diff --git a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpConfiguration.java b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpConfiguration.java index b8cbb8162..24a5025a2 100644 --- a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpConfiguration.java +++ b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpConfiguration.java @@ -14,8 +14,13 @@ import java.util.concurrent.Executor; import java.util.concurrent.ScheduledExecutorService; import org.eclipse.hawkbit.api.ArtifactUrlHandler; +import org.eclipse.hawkbit.api.HostnameResolver; +import org.eclipse.hawkbit.cache.CacheConstants; import org.eclipse.hawkbit.dmf.amqp.api.AmqpSettings; +import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.ControllerManagement; +import org.eclipse.hawkbit.repository.EntityFactory; +import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.security.DdiSecurityProperties; import org.eclipse.hawkbit.security.SystemSecurityContext; @@ -40,6 +45,7 @@ import org.springframework.boot.autoconfigure.amqp.RabbitProperties; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cache.Cache; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.retry.backoff.ExponentialBackOffPolicy; @@ -49,7 +55,7 @@ import org.springframework.util.ErrorHandler; import com.google.common.collect.Maps; /** - * Spring configuration for AMQP 0.9 based DMF communication for indirect device + * Spring configuration for AMQP based DMF communication for indirect device * integration. * */ @@ -252,17 +258,51 @@ public class AmqpConfiguration { } /** - * Create amqp handler service bean. + * Create AMQP handler service bean. * + * @param rabbitTemplate + * for converting messages * @param amqpMessageDispatcherService * to sending events to DMF client + * @param controllerManagement + * for target repo access + * @param entityFactory + * to create entities * * @return handler service bean */ @Bean - public AmqpMessageHandlerService amqpMessageHandlerService( - final AmqpMessageDispatcherService amqpMessageDispatcherService) { - return new AmqpMessageHandlerService(rabbitTemplate(), amqpMessageDispatcherService); + public AmqpMessageHandlerService amqpMessageHandlerService(final RabbitTemplate rabbitTemplate, + final AmqpMessageDispatcherService amqpMessageDispatcherService, + final ControllerManagement controllerManagement, final EntityFactory entityFactory) { + return new AmqpMessageHandlerService(rabbitTemplate, amqpMessageDispatcherService, controllerManagement, + entityFactory); + } + + /** + * Create AMQP handler service bean for authentication messages. + * + * @param rabbitTemplate + * for converting messages + * @param authenticationManager + * for target authentication + * @param artifactManagement + * for artifact URI generation + * @param cache + * for download IDs + * @param hostnameResolver + * for resolving the host for downloads + * @param controllerManagement + * for target repo access + * @return handler service bean + */ + @Bean + public AmqpAuthenticationMessageHandler amqpAuthenticationMessageHandler(final RabbitTemplate rabbitTemplate, + final AmqpControllerAuthentication authenticationManager, final ArtifactManagement artifactManagement, + @Qualifier(CacheConstants.DOWNLOAD_ID_CACHE) final Cache cache, final HostnameResolver hostnameResolver, + final ControllerManagement controllerManagement) { + return new AmqpAuthenticationMessageHandler(rabbitTemplate, authenticationManager, artifactManagement, cache, + hostnameResolver, controllerManagement); } /** @@ -292,18 +332,21 @@ public class AmqpConfiguration { @Bean @ConditionalOnMissingBean(AmqpControllerAuthentication.class) - public AmqpControllerAuthentication amqpControllerAuthentication(final ControllerManagement controllerManagement, + public AmqpControllerAuthentication amqpControllerAuthentication(final SystemManagement systemManagement, + final ControllerManagement controllerManagement, final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware, final DdiSecurityProperties ddiSecruityProperties, final SystemSecurityContext systemSecurityContext) { - return new AmqpControllerAuthentication(controllerManagement, tenantConfigurationManagement, tenantAware, - ddiSecruityProperties, systemSecurityContext); + return new AmqpControllerAuthentication(systemManagement, controllerManagement, tenantConfigurationManagement, + tenantAware, ddiSecruityProperties, systemSecurityContext); } @Bean @ConditionalOnMissingBean(AmqpMessageDispatcherService.class) public AmqpMessageDispatcherService amqpMessageDispatcherService(final RabbitTemplate rabbitTemplate, - final AmqpSenderService amqpSenderService, final ArtifactUrlHandler artifactUrlHandler) { - return new AmqpMessageDispatcherService(rabbitTemplate, amqpSenderService, artifactUrlHandler); + final AmqpSenderService amqpSenderService, final ArtifactUrlHandler artifactUrlHandler, + final SystemSecurityContext systemSecurityContext, final SystemManagement systemManagement) { + return new AmqpMessageDispatcherService(rabbitTemplate, amqpSenderService, artifactUrlHandler, + systemSecurityContext, systemManagement); } private static Map getTTLMaxArgsAuthenticationQueue() { diff --git a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthentication.java b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthentication.java index 2e604aad1..64abd2778 100644 --- a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthentication.java +++ b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthentication.java @@ -8,7 +8,6 @@ */ package org.eclipse.hawkbit.amqp; -import java.util.ArrayList; import java.util.List; import javax.annotation.PostConstruct; @@ -16,6 +15,7 @@ import javax.annotation.PostConstruct; import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken; import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails; import org.eclipse.hawkbit.repository.ControllerManagement; +import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.security.ControllerPreAuthenticateSecurityTokenFilter; import org.eclipse.hawkbit.security.ControllerPreAuthenticatedAnonymousDownload; @@ -32,6 +32,8 @@ import org.slf4j.LoggerFactory; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; +import com.google.common.collect.Lists; + /** * * A controller which handles the DMF AMQP authentication. @@ -42,10 +44,12 @@ public class AmqpControllerAuthentication { private final PreAuthTokenSourceTrustAuthenticationProvider preAuthenticatedAuthenticationProvider = new PreAuthTokenSourceTrustAuthenticationProvider(); - private final List filterChain = new ArrayList<>(); + private List filterChain; private final ControllerManagement controllerManagement; + private final SystemManagement systemManagement; + private final TenantConfigurationManagement tenantConfigurationManagement; private final TenantAware tenantAware; @@ -57,6 +61,7 @@ public class AmqpControllerAuthentication { /** * Constructor. * + * @param systemManagement * @param controllerManagement * @param tenantConfigurationManagement * @param tenantAware @@ -66,10 +71,12 @@ public class AmqpControllerAuthentication { * @param systemSecurityContext * security context */ - public AmqpControllerAuthentication(final ControllerManagement controllerManagement, + public AmqpControllerAuthentication(final SystemManagement systemManagement, + final ControllerManagement controllerManagement, final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware, final DdiSecurityProperties ddiSecruityProperties, final SystemSecurityContext systemSecurityContext) { this.controllerManagement = controllerManagement; + this.systemManagement = systemManagement; this.tenantConfigurationManagement = tenantConfigurationManagement; this.tenantAware = tenantAware; this.ddiSecruityProperties = ddiSecruityProperties; @@ -85,6 +92,8 @@ public class AmqpControllerAuthentication { } private void addFilter() { + filterChain = Lists.newArrayListWithExpectedSize(5); + final ControllerPreAuthenticatedGatewaySecurityTokenFilter gatewaySecurityTokenFilter = new ControllerPreAuthenticatedGatewaySecurityTokenFilter( tenantConfigurationManagement, tenantAware, systemSecurityContext); filterChain.add(gatewaySecurityTokenFilter); @@ -106,13 +115,15 @@ public class AmqpControllerAuthentication { } /** - * Performs authentication with the secruity token. + * Performs authentication with the security token. * * @param secruityToken * the authentication request object - * @return the authentfication object + * @return the authentication object */ public Authentication doAuthenticate(final TenantSecurityToken secruityToken) { + resolveTenant(secruityToken); + PreAuthenticatedAuthenticationToken authentication = new PreAuthenticatedAuthenticationToken(null, null); for (final PreAuthentificationFilter filter : filterChain) { final PreAuthenticatedAuthenticationToken authenticationRest = createAuthentication(filter, secruityToken); @@ -126,6 +137,14 @@ public class AmqpControllerAuthentication { } + private void resolveTenant(final TenantSecurityToken securityToken) { + if (securityToken.getTenant() == null) { + securityToken.setTenant(systemSecurityContext + .runAsSystem(() -> systemManagement.getTenantMetadata(securityToken.getTenantId()).getTenant())); + } + + } + private static PreAuthenticatedAuthenticationToken createAuthentication(final PreAuthentificationFilter filter, final TenantSecurityToken secruityToken) { diff --git a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java index 1d24d5846..a4ea97e72 100644 --- a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java +++ b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java @@ -14,8 +14,10 @@ import java.util.Collections; import java.util.List; import java.util.stream.Collectors; +import org.eclipse.hawkbit.api.ApiType; import org.eclipse.hawkbit.api.ArtifactUrlHandler; -import org.eclipse.hawkbit.api.UrlProtocol; +import org.eclipse.hawkbit.api.URLPlaceholder; +import org.eclipse.hawkbit.api.URLPlaceholder.SoftwareData; import org.eclipse.hawkbit.dmf.amqp.api.EventTopic; import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey; import org.eclipse.hawkbit.dmf.amqp.api.MessageType; @@ -25,8 +27,11 @@ import org.eclipse.hawkbit.dmf.json.model.DownloadAndUpdateRequest; import org.eclipse.hawkbit.dmf.json.model.SoftwareModule; import org.eclipse.hawkbit.eventbus.EventSubscriber; import org.eclipse.hawkbit.eventbus.event.CancelTargetAssignmentEvent; +import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.eventbus.event.TargetAssignDistributionSetEvent; import org.eclipse.hawkbit.repository.model.LocalArtifact; +import org.eclipse.hawkbit.repository.model.Target; +import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.util.IpUtil; import org.springframework.amqp.core.Message; import org.springframework.amqp.core.MessageProperties; @@ -47,6 +52,8 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { private final ArtifactUrlHandler artifactUrlHandler; private final AmqpSenderService amqpSenderService; + private final SystemSecurityContext systemSecurityContext; + private final SystemManagement systemManagement; /** * Constructor. @@ -57,12 +64,19 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { * to send AMQP message * @param artifactUrlHandler * for generating download URLs + * @param systemSecurityContext + * for execution with system permissions + * @param systemManagement + * to access to tenant metadata */ public AmqpMessageDispatcherService(final RabbitTemplate rabbitTemplate, final AmqpSenderService amqpSenderService, - final ArtifactUrlHandler artifactUrlHandler) { + final ArtifactUrlHandler artifactUrlHandler, final SystemSecurityContext systemSecurityContext, + final SystemManagement systemManagement) { super(rabbitTemplate); this.artifactUrlHandler = artifactUrlHandler; this.amqpSenderService = amqpSenderService; + this.systemSecurityContext = systemSecurityContext; + this.systemManagement = systemManagement; } /** @@ -74,20 +88,24 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { */ @Subscribe public void targetAssignDistributionSet(final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent) { - final URI targetAdress = targetAssignDistributionSetEvent.getTargetAdress(); + final URI targetAdress = targetAssignDistributionSetEvent.getTarget().getTargetInfo().getAddress(); if (!IpUtil.isAmqpUri(targetAdress)) { return; } - final String controllerId = targetAssignDistributionSetEvent.getControllerId(); + final String controllerId = targetAssignDistributionSetEvent.getTarget().getControllerId(); final Collection modules = targetAssignDistributionSetEvent .getSoftwareModules(); final DownloadAndUpdateRequest downloadAndUpdateRequest = new DownloadAndUpdateRequest(); downloadAndUpdateRequest.setActionId(targetAssignDistributionSetEvent.getActionId()); - downloadAndUpdateRequest.setTargetSecurityToken(targetAssignDistributionSetEvent.getTargetToken()); + + final String targetSecurityToken = systemSecurityContext + .runAsSystem(targetAssignDistributionSetEvent.getTarget()::getSecurityToken); + downloadAndUpdateRequest.setTargetSecurityToken(targetSecurityToken); for (final org.eclipse.hawkbit.repository.model.SoftwareModule softwareModule : modules) { - final SoftwareModule amqpSoftwareModule = convertToAmqpSoftwareModule(controllerId, softwareModule); + final SoftwareModule amqpSoftwareModule = convertToAmqpSoftwareModule( + targetAssignDistributionSetEvent.getTarget(), softwareModule); downloadAndUpdateRequest.addSoftwareModule(amqpSoftwareModule); } @@ -133,51 +151,42 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { return messageProperties; } - private SoftwareModule convertToAmqpSoftwareModule(final String targetId, + private SoftwareModule convertToAmqpSoftwareModule(final Target target, final org.eclipse.hawkbit.repository.model.SoftwareModule softwareModule) { final SoftwareModule amqpSoftwareModule = new SoftwareModule(); amqpSoftwareModule.setModuleId(softwareModule.getId()); amqpSoftwareModule.setModuleType(softwareModule.getType().getKey()); amqpSoftwareModule.setModuleVersion(softwareModule.getVersion()); - final List artifacts = convertArtifacts(targetId, softwareModule.getLocalArtifacts()); + final List artifacts = convertArtifacts(target, softwareModule.getLocalArtifacts()); amqpSoftwareModule.setArtifacts(artifacts); return amqpSoftwareModule; } - private List convertArtifacts(final String targetId, final List localArtifacts) { + private List convertArtifacts(final Target target, final List localArtifacts) { if (localArtifacts.isEmpty()) { return Collections.emptyList(); } - return localArtifacts.stream().map(localArtifact -> convertArtifact(targetId, localArtifact)) + return localArtifacts.stream().map(localArtifact -> convertArtifact(target, localArtifact)) .collect(Collectors.toList()); } - private Artifact convertArtifact(final String targetId, final LocalArtifact localArtifact) { + private Artifact convertArtifact(final Target target, final LocalArtifact localArtifact) { final Artifact artifact = new Artifact(); - if (artifactUrlHandler.protocolSupported(UrlProtocol.COAP)) { - artifact.getUrls().put(Artifact.UrlProtocol.COAP, - artifactUrlHandler.getUrl(targetId, localArtifact.getSoftwareModule().getId(), - localArtifact.getFilename(), localArtifact.getSha1Hash(), UrlProtocol.COAP)); - } - - if (artifactUrlHandler.protocolSupported(UrlProtocol.HTTP)) { - artifact.getUrls().put(Artifact.UrlProtocol.HTTP, - artifactUrlHandler.getUrl(targetId, localArtifact.getSoftwareModule().getId(), - localArtifact.getFilename(), localArtifact.getSha1Hash(), UrlProtocol.HTTP)); - } - - if (artifactUrlHandler.protocolSupported(UrlProtocol.HTTPS)) { - artifact.getUrls().put(Artifact.UrlProtocol.HTTPS, - artifactUrlHandler.getUrl(targetId, localArtifact.getSoftwareModule().getId(), - localArtifact.getFilename(), localArtifact.getSha1Hash(), UrlProtocol.HTTPS)); - } + artifact.setUrls(artifactUrlHandler + .getUrls(new URLPlaceholder(systemManagement.getTenantMetadata().getTenant(), + systemManagement.getTenantMetadata().getId(), target.getControllerId(), target.getId(), + new SoftwareData(localArtifact.getSoftwareModule().getId(), localArtifact.getFilename(), + localArtifact.getId(), localArtifact.getSha1Hash())), + ApiType.DMF) + .stream().collect(Collectors.toMap(e -> e.getProtocol(), e -> e.getRef()))); artifact.setFilename(localArtifact.getFilename()); artifact.setHashes(new ArtifactHash(localArtifact.getSha1Hash(), localArtifact.getMd5Hash())); artifact.setSize(localArtifact.getSize()); return artifact; } + } diff --git a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java index e8387e0c2..56ecc84cb 100644 --- a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java +++ b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java @@ -9,7 +9,6 @@ package org.eclipse.hawkbit.amqp; import java.net.URI; -import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.List; @@ -18,68 +17,45 @@ import java.util.UUID; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; -import org.eclipse.hawkbit.api.HostnameResolver; -import org.eclipse.hawkbit.artifact.repository.model.DbArtifact; -import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash; -import org.eclipse.hawkbit.cache.CacheConstants; -import org.eclipse.hawkbit.cache.DownloadArtifactCache; -import org.eclipse.hawkbit.cache.DownloadType; import org.eclipse.hawkbit.dmf.amqp.api.EventTopic; import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey; import org.eclipse.hawkbit.dmf.amqp.api.MessageType; import org.eclipse.hawkbit.dmf.json.model.ActionUpdateStatus; -import org.eclipse.hawkbit.dmf.json.model.Artifact; -import org.eclipse.hawkbit.dmf.json.model.ArtifactHash; -import org.eclipse.hawkbit.dmf.json.model.DownloadResponse; -import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken; -import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken.FileResource; import org.eclipse.hawkbit.eventbus.event.CancelTargetAssignmentEvent; import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions; import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails; -import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.ControllerManagement; import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.RepositoryConstants; import org.eclipse.hawkbit.repository.eventbus.event.TargetAssignDistributionSetEvent; -import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.TenantNotExistException; import org.eclipse.hawkbit.repository.exception.TooManyStatusEntriesException; 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.DistributionSet; -import org.eclipse.hawkbit.repository.model.LocalArtifact; import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.Target; -import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.util.IpUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.AmqpRejectAndDontRequeueException; import org.springframework.amqp.core.Message; -import org.springframework.amqp.core.MessageProperties; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.cache.Cache; -import org.springframework.http.HttpStatus; import org.springframework.messaging.handler.annotation.Header; import org.springframework.security.authentication.AnonymousAuthenticationToken; -import org.springframework.security.authentication.AuthenticationServiceException; -import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.security.authentication.CredentialsExpiredException; import org.springframework.security.core.Authentication; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextImpl; -import org.springframework.web.util.UriComponentsBuilder; /** * - * {@link AmqpMessageHandlerService} handles all incoming AMQP messages for the - * queue which is configure for the property hawkbit.dmf.rabbitmq.receiverQueue. + * {@link AmqpMessageHandlerService} handles all incoming target interaction + * AMQP messages (e.g. create target, check for updates etc.) for the queue + * which is configured for the property hawkbit.dmf.rabbitmq.receiverQueue. * */ public class AmqpMessageHandlerService extends BaseAmqpService { @@ -88,40 +64,29 @@ public class AmqpMessageHandlerService extends BaseAmqpService { private final AmqpMessageDispatcherService amqpMessageDispatcherService; - @Autowired - private ControllerManagement controllerManagement; + private final ControllerManagement controllerManagement; - @Autowired - private AmqpControllerAuthentication authenticationManager; - - @Autowired - private ArtifactManagement artifactManagement; - - @Autowired - @Qualifier(CacheConstants.DOWNLOAD_ID_CACHE) - private Cache cache; - - @Autowired - private HostnameResolver hostnameResolver; - - @Autowired - private EntityFactory entityFactory; - - @Autowired - private SystemSecurityContext systemSecurityContext; + private final EntityFactory entityFactory; /** * Constructor. * - * @param defaultTemplate - * the configured amqp template. + * @param rabbitTemplate + * for converting messages * @param amqpMessageDispatcherService * to sending events to DMF client + * @param controllerManagement + * for target repo access + * @param entityFactory + * to create entities */ - public AmqpMessageHandlerService(final RabbitTemplate defaultTemplate, - final AmqpMessageDispatcherService amqpMessageDispatcherService) { - super(defaultTemplate); + public AmqpMessageHandlerService(final RabbitTemplate rabbitTemplate, + final AmqpMessageDispatcherService amqpMessageDispatcherService, + final ControllerManagement controllerManagement, final EntityFactory entityFactory) { + super(rabbitTemplate); this.amqpMessageDispatcherService = amqpMessageDispatcherService; + this.controllerManagement = controllerManagement; + this.entityFactory = entityFactory; } /** @@ -142,28 +107,6 @@ public class AmqpMessageHandlerService extends BaseAmqpService { return onMessage(message, type, tenant, getRabbitTemplate().getConnectionFactory().getVirtualHost()); } - /** - * Executed on a authentication request. - * - * @param message - * the amqp message - * @return the rpc message back to supplier. - */ - @RabbitListener(queues = "${hawkbit.dmf.rabbitmq.authenticationReceiverQueue}", containerFactory = "listenerContainerFactory") - public Message onAuthenticationRequest(final Message message) { - checkContentTypeJson(message); - final SecurityContext oldContext = SecurityContextHolder.getContext(); - try { - return handleAuthentifiactionMessage(message); - } catch (final IllegalArgumentException ex) { - throw new AmqpRejectAndDontRequeueException("Invalid message!", ex); - } catch (final TenantNotExistException | TooManyStatusEntriesException e) { - throw new AmqpRejectAndDontRequeueException(e); - } finally { - SecurityContextHolder.setContext(oldContext); - } - } - /** * * Executed if a amqp message arrives. * @@ -206,108 +149,6 @@ public class AmqpMessageHandlerService extends BaseAmqpService { return null; } - private Message handleAuthentifiactionMessage(final Message message) { - final DownloadResponse authentificationResponse = new DownloadResponse(); - final MessageProperties messageProperties = message.getMessageProperties(); - final TenantSecurityToken secruityToken = convertMessage(message, TenantSecurityToken.class); - final FileResource fileResource = secruityToken.getFileResource(); - try { - SecurityContextHolder.getContext().setAuthentication(authenticationManager.doAuthenticate(secruityToken)); - - final LocalArtifact localArtifact = findLocalArtifactByFileResource(fileResource); - - if (localArtifact == null) { - LOG.info("target {} requested file resource {} which does not exists to download", - secruityToken.getControllerId(), fileResource); - throw new EntityNotFoundException(); - } - - checkIfArtifactIsAssignedToTarget(secruityToken, localArtifact); - - final Artifact artifact = convertDbArtifact(artifactManagement.loadLocalArtifactBinary(localArtifact)); - if (artifact == null) { - throw new EntityNotFoundException(); - } - authentificationResponse.setArtifact(artifact); - final String downloadId = UUID.randomUUID().toString(); - // SHA1 key is set, download by SHA1 - final DownloadArtifactCache downloadCache = new DownloadArtifactCache(DownloadType.BY_SHA1, - localArtifact.getSha1Hash()); - cache.put(downloadId, downloadCache); - authentificationResponse - .setDownloadUrl(UriComponentsBuilder.fromUri(hostnameResolver.resolveHostname().toURI()) - .path("/api/v1/downloadserver/downloadId/").path(downloadId).build().toUriString()); - authentificationResponse.setResponseCode(HttpStatus.OK.value()); - } catch (final BadCredentialsException | AuthenticationServiceException | CredentialsExpiredException e) { - LOG.error("Login failed", e); - authentificationResponse.setResponseCode(HttpStatus.FORBIDDEN.value()); - authentificationResponse.setMessage("Login failed"); - } catch (final URISyntaxException e) { - LOG.error("URI build exception", e); - authentificationResponse.setResponseCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); - authentificationResponse.setMessage("Building download URI failed"); - } catch (final EntityNotFoundException e) { - final String errorMessage = "Artifact for resource " + fileResource + "not found "; - LOG.warn(errorMessage, e); - authentificationResponse.setResponseCode(HttpStatus.NOT_FOUND.value()); - authentificationResponse.setMessage(errorMessage); - } - - return getMessageConverter().toMessage(authentificationResponse, messageProperties); - } - - /** - * check action for this download purposes, the method will throw an - * EntityNotFoundException in case the controller is not allowed to download - * this file because it's not assigned to an action and not assigned to this - * controller. Otherwise no controllerId is set = anonymous download - * - * @param secruityToken - * the security token which holds the target ID to check on - * @param localArtifact - * the local artifact to verify if the given target is allowed to - * download this artifact - */ - private void checkIfArtifactIsAssignedToTarget(final TenantSecurityToken secruityToken, - final LocalArtifact localArtifact) { - final String controllerId = secruityToken.getControllerId(); - if (controllerId == null) { - LOG.info("anonymous download no authentication check for artifact {}", localArtifact); - return; - } - LOG.debug("no anonymous download request, doing authentication check for target {} and artifact {}", - controllerId, localArtifact); - if (!controllerManagement.hasTargetArtifactAssigned(controllerId, localArtifact)) { - LOG.info("target {} tried to download artifact {} which is not assigned to the target", controllerId, - localArtifact); - throw new EntityNotFoundException(); - } - LOG.info("download security check for target {} and artifact {} granted", controllerId, localArtifact); - } - - private LocalArtifact findLocalArtifactByFileResource(final FileResource fileResource) { - if (fileResource.getSha1() != null) { - return artifactManagement.findFirstLocalArtifactsBySHA1(fileResource.getSha1()); - } else if (fileResource.getFilename() != null) { - return artifactManagement.findLocalArtifactByFilename(fileResource.getFilename()).stream().findFirst() - .orElse(null); - } else if (fileResource.getSoftwareModuleFilenameResource() != null) { - return artifactManagement - .findByFilenameAndSoftwareModule(fileResource.getSoftwareModuleFilenameResource().getFilename(), - fileResource.getSoftwareModuleFilenameResource().getSoftwareModuleId()) - .stream().findFirst().orElse(null); - } - return null; - } - - private static Artifact convertDbArtifact(final DbArtifact dbArtifact) { - final Artifact artifact = new Artifact(); - artifact.setSize(dbArtifact.getSize()); - final DbArtifactHash dbArtifactHash = dbArtifact.getHashes(); - artifact.setHashes(new ArtifactHash(dbArtifactHash.getSha1(), dbArtifactHash.getMd5())); - return artifact; - } - private static void setSecurityContext(final Authentication authentication) { final SecurityContextImpl securityContextImpl = new SecurityContextImpl(); securityContextImpl.setAuthentication(authentication); @@ -361,10 +202,8 @@ public class AmqpMessageHandlerService extends BaseAmqpService { final DistributionSet distributionSet = action.get().getDistributionSet(); final List softwareModuleList = controllerManagement .findSoftwareModulesByDistributionSet(distributionSet); - final String targetSecurityToken = systemSecurityContext.runAsSystem(() -> target.getSecurityToken()); amqpMessageDispatcherService.targetAssignDistributionSet(new TargetAssignDistributionSetEvent( - target.getOptLockRevision(), target.getTenant(), target.getControllerId(), action.get().getId(), - softwareModuleList, target.getTargetInfo().getAddress(), targetSecurityToken)); + target.getOptLockRevision(), target.getTenant(), target, action.get().getId(), softwareModuleList)); } @@ -481,7 +320,8 @@ public class AmqpMessageHandlerService extends BaseAmqpService { return action; } - private void handleCancelRejected(final Message message, final Action action, final ActionStatus actionStatus) { + private static void handleCancelRejected(final Message message, final Action action, + final ActionStatus actionStatus) { if (action.isCancelingOrCanceled()) { actionStatus.setStatus(Status.WARNING); @@ -495,39 +335,4 @@ public class AmqpMessageHandlerService extends BaseAmqpService { } } - private static void checkContentTypeJson(final Message message) { - final MessageProperties messageProperties = message.getMessageProperties(); - if (messageProperties.getContentType() != null && messageProperties.getContentType().contains("json")) { - return; - } - throw new AmqpRejectAndDontRequeueException("Content-Type is not JSON compatible"); - } - - void setControllerManagement(final ControllerManagement controllerManagement) { - this.controllerManagement = controllerManagement; - } - - void setHostnameResolver(final HostnameResolver hostnameResolver) { - this.hostnameResolver = hostnameResolver; - } - - void setAuthenticationManager(final AmqpControllerAuthentication authenticationManager) { - this.authenticationManager = authenticationManager; - } - - void setArtifactManagement(final ArtifactManagement artifactManagement) { - this.artifactManagement = artifactManagement; - } - - void setCache(final Cache cache) { - this.cache = cache; - } - - void setEntityFactory(final EntityFactory entityFactory) { - this.entityFactory = entityFactory; - } - - void setSystemSecurityContext(final SystemSecurityContext systemSecurityContext) { - this.systemSecurityContext = systemSecurityContext; - } } diff --git a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/BaseAmqpService.java b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/BaseAmqpService.java index b11d5a437..b0bd1f962 100644 --- a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/BaseAmqpService.java +++ b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/BaseAmqpService.java @@ -17,6 +17,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.AmqpRejectAndDontRequeueException; import org.springframework.amqp.core.Message; +import org.springframework.amqp.core.MessageProperties; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.support.converter.AbstractJavaTypeMapper; import org.springframework.amqp.support.converter.MessageConverter; @@ -39,6 +40,14 @@ public class BaseAmqpService { this.rabbitTemplate = rabbitTemplate; } + protected static void checkContentTypeJson(final Message message) { + final MessageProperties messageProperties = message.getMessageProperties(); + if (messageProperties.getContentType() != null && messageProperties.getContentType().contains("json")) { + return; + } + throw new AmqpRejectAndDontRequeueException("Content-Type is not JSON compatible"); + } + /** * Is needed to convert a incoming message to is originally object type. * @@ -98,7 +107,7 @@ public class BaseAmqpService { return value.toString(); } - protected final void logAndThrowMessageError(final Message message, final String error) { + protected static final void logAndThrowMessageError(final Message message, final String error) { LOGGER.warn("Warning! \"{}\" reported by message: {}", error, message); throw new AmqpRejectAndDontRequeueException(error); } diff --git a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthenticationTest.java b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthenticationTest.java index 9c1265960..c2ac1c35b 100644 --- a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthenticationTest.java +++ b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthenticationTest.java @@ -16,6 +16,11 @@ import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import java.net.URL; + +import org.eclipse.hawkbit.api.HostnameResolver; +import org.eclipse.hawkbit.artifact.repository.model.DbArtifact; +import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash; import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey; import org.eclipse.hawkbit.dmf.amqp.api.MessageType; import org.eclipse.hawkbit.dmf.json.model.DownloadResponse; @@ -23,8 +28,16 @@ import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken; import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken.FileResource; import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.ControllerManagement; +import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement; +import org.eclipse.hawkbit.repository.jpa.JpaEntityFactory; +import org.eclipse.hawkbit.repository.jpa.model.JpaLocalArtifact; +import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule; +import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType; +import org.eclipse.hawkbit.repository.model.LocalArtifact; +import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TenantConfigurationValue; +import org.eclipse.hawkbit.repository.model.TenantMetaData; import org.eclipse.hawkbit.security.DdiSecurityProperties; import org.eclipse.hawkbit.security.DdiSecurityProperties.Authentication.Anonymous; import org.eclipse.hawkbit.security.DdiSecurityProperties.Rp; @@ -33,11 +46,15 @@ import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; import org.springframework.amqp.core.Message; import org.springframework.amqp.core.MessageProperties; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; import org.springframework.amqp.support.converter.MessageConverter; +import org.springframework.cache.Cache; import org.springframework.http.HttpStatus; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.Authentication; @@ -54,15 +71,44 @@ import ru.yandex.qatools.allure.annotations.Stories; */ @Features("Component Tests - Device Management Federation API") @Stories("AmqpController Authentication Test") +@RunWith(MockitoJUnitRunner.class) public class AmqpControllerAuthenticationTest { + private static final String SHA1 = "12345"; + private static final Long ARTIFACT_ID = 1123L; + private static final Long ARTIFACT_SIZE = 6666L; private static final String TENANT = "DEFAULT"; - private static String CONTROLLLER_ID = "123"; + private static final Long TENANT_ID = 123L; + private static final String CONTROLLER_ID = "123"; + private static final Long TARGET_ID = 123L; private AmqpMessageHandlerService amqpMessageHandlerService; + private AmqpAuthenticationMessageHandler amqpAuthenticationMessageHandlerService; + private MessageConverter messageConverter; - private TenantConfigurationManagement tenantConfigurationManagement; + private AmqpControllerAuthentication authenticationManager; + @Mock + private TenantConfigurationManagement tenantConfigurationManagementMock; + + @Mock + private SystemManagement systemManagement; + + @Mock + private Cache cacheMock; + + @Mock + private HostnameResolver hostnameResolverMock; + + @Mock + private ArtifactManagement artifactManagementMock; + + @Mock + private ControllerManagement controllerManagementMock; + + @Mock + private Target targteMock; + private static final TenantConfigurationValue CONFIG_VALUE_FALSE = TenantConfigurationValue . builder().value(Boolean.FALSE).build(); @@ -74,8 +120,6 @@ public class AmqpControllerAuthenticationTest { messageConverter = new Jackson2JsonMessageConverter(); final RabbitTemplate rabbitTemplate = mock(RabbitTemplate.class); when(rabbitTemplate.getMessageConverter()).thenReturn(messageConverter); - amqpMessageHandlerService = new AmqpMessageHandlerService(rabbitTemplate, - mock(AmqpMessageDispatcherService.class)); final DdiSecurityProperties secruityProperties = mock(DdiSecurityProperties.class); final Rp rp = mock(Rp.class); @@ -88,30 +132,57 @@ public class AmqpControllerAuthenticationTest { when(ddiAuthentication.getAnonymous()).thenReturn(anonymous); when(anonymous.isEnabled()).thenReturn(false); - tenantConfigurationManagement = mock(TenantConfigurationManagement.class); - - when(tenantConfigurationManagement.getConfigurationValue(any(), eq(Boolean.class))) + when(tenantConfigurationManagementMock.getConfigurationValue(any(), eq(Boolean.class))) .thenReturn(CONFIG_VALUE_FALSE); final ControllerManagement controllerManagement = mock(ControllerManagement.class); - when(controllerManagement.getSecurityTokenByControllerId(anyString())).thenReturn(CONTROLLLER_ID); - amqpMessageHandlerService.setArtifactManagement(mock(ArtifactManagement.class)); + when(controllerManagement.findByControllerId(anyString())).thenReturn(targteMock); + when(controllerManagement.findByTargetId(any(Long.class))).thenReturn(targteMock); + + when(targteMock.getSecurityToken()).thenReturn(CONTROLLER_ID); + when(targteMock.getControllerId()).thenReturn(CONTROLLER_ID); final SecurityContextTenantAware tenantAware = new SecurityContextTenantAware(); final SystemSecurityContext systemSecurityContext = new SystemSecurityContext(tenantAware); - authenticationManager = new AmqpControllerAuthentication(controllerManagement, tenantConfigurationManagement, - tenantAware, secruityProperties, systemSecurityContext); + final TenantMetaData tenantMetaData = mock(TenantMetaData.class); + when(tenantMetaData.getTenant()).thenReturn(TENANT); + when(systemManagement.getTenantMetadata(TENANT_ID)).thenReturn(tenantMetaData); + + authenticationManager = new AmqpControllerAuthentication(systemManagement, controllerManagement, + tenantConfigurationManagementMock, tenantAware, secruityProperties, systemSecurityContext); authenticationManager.postConstruct(); - amqpMessageHandlerService.setAuthenticationManager(authenticationManager); + + final LocalArtifact testArtifact = new JpaLocalArtifact("afilename", "afilename", new JpaSoftwareModule( + new JpaSoftwareModuleType("a key", "a name", null, 1), "a name", null, null, null)); + + when(artifactManagementMock.findLocalArtifact(ARTIFACT_ID)).thenReturn(testArtifact); + when(artifactManagementMock.findFirstLocalArtifactsBySHA1(SHA1)).thenReturn(testArtifact); + + final DbArtifact artifact = new DbArtifact(); + artifact.setSize(ARTIFACT_SIZE); + artifact.setHashes(new DbArtifactHash("sha1 test", "md5 test")); + when(artifactManagementMock.loadLocalArtifactBinary(testArtifact)).thenReturn(artifact); + + amqpMessageHandlerService = new AmqpMessageHandlerService(rabbitTemplate, + mock(AmqpMessageDispatcherService.class), controllerManagementMock, new JpaEntityFactory()); + + amqpAuthenticationMessageHandlerService = new AmqpAuthenticationMessageHandler(rabbitTemplate, + authenticationManager, artifactManagementMock, cacheMock, hostnameResolverMock, + controllerManagementMock); + + when(hostnameResolverMock.resolveHostname()).thenReturn(new URL("http://localhost")); + + when(controllerManagementMock.hasTargetArtifactAssigned(TARGET_ID, testArtifact)).thenReturn(true); + when(controllerManagementMock.hasTargetArtifactAssigned(CONTROLLER_ID, testArtifact)).thenReturn(true); } @Test @Description("Tests authentication manager without principal") public void testAuthenticationeBadCredantialsWithoutPricipal() { - final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, CONTROLLLER_ID, - FileResource.createFileResourceBySha1("12345")); + final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, TENANT_ID, CONTROLLER_ID, TARGET_ID, + FileResource.createFileResourceBySha1(SHA1)); try { authenticationManager.doAuthenticate(securityToken); fail("BadCredentialsException was excepeted since principal was missing"); @@ -124,12 +195,12 @@ public class AmqpControllerAuthenticationTest { @Test @Description("Tests authentication manager without wrong credential") public void testAuthenticationBadCredantialsWithWrongCredential() { - final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, CONTROLLLER_ID, - FileResource.createFileResourceBySha1("12345")); - when(tenantConfigurationManagement.getConfigurationValue( + final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, TENANT_ID, CONTROLLER_ID, TARGET_ID, + FileResource.createFileResourceBySha1(SHA1)); + when(tenantConfigurationManagementMock.getConfigurationValue( eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class))) .thenReturn(CONFIG_VALUE_TRUE); - securityToken.getHeaders().put(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken 12" + CONTROLLLER_ID); + securityToken.putHeader(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken 12" + CONTROLLER_ID); try { authenticationManager.doAuthenticate(securityToken); fail("BadCredentialsException was excepeted due to wrong credential"); @@ -142,12 +213,12 @@ public class AmqpControllerAuthenticationTest { @Test @Description("Tests authentication successfull") public void testSuccessfullAuthentication() { - final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, CONTROLLLER_ID, - FileResource.createFileResourceBySha1("12345")); - when(tenantConfigurationManagement.getConfigurationValue( + final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, TENANT_ID, CONTROLLER_ID, TARGET_ID, + FileResource.createFileResourceBySha1(SHA1)); + when(tenantConfigurationManagementMock.getConfigurationValue( eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class))) .thenReturn(CONFIG_VALUE_TRUE); - securityToken.getHeaders().put(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken " + CONTROLLLER_ID); + securityToken.putHeader(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken " + CONTROLLER_ID); final Authentication authentication = authenticationManager.doAuthenticate(securityToken); assertThat(authentication).isNotNull(); } @@ -157,13 +228,13 @@ public class AmqpControllerAuthenticationTest { public void testAuthenticationMessageBadCredantialsWithoutPricipal() { final MessageProperties messageProperties = createMessageProperties(null); - final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, CONTROLLLER_ID, - FileResource.createFileResourceBySha1("12345")); + final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, TENANT_ID, CONTROLLER_ID, TARGET_ID, + FileResource.createFileResourceBySha1(SHA1)); final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken, messageProperties); // test - final Message onMessage = amqpMessageHandlerService.onAuthenticationRequest(message); + final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message); // verify final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage); @@ -175,17 +246,17 @@ public class AmqpControllerAuthenticationTest { @Description("Tests authentication message without wrong credential") public void testAuthenticationMessageBadCredantialsWithWrongCredential() { final MessageProperties messageProperties = createMessageProperties(null); - final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, CONTROLLLER_ID, - FileResource.createFileResourceBySha1("12345")); - when(tenantConfigurationManagement.getConfigurationValue( + final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, TENANT_ID, CONTROLLER_ID, TARGET_ID, + FileResource.createFileResourceBySha1(SHA1)); + when(tenantConfigurationManagementMock.getConfigurationValue( eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class))) .thenReturn(CONFIG_VALUE_TRUE); - securityToken.getHeaders().put(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken 12" + CONTROLLLER_ID); + securityToken.putHeader(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken 12" + CONTROLLER_ID); final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken, messageProperties); // test - final Message onMessage = amqpMessageHandlerService.onAuthenticationRequest(message); + final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message); // verify final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage); @@ -195,24 +266,81 @@ public class AmqpControllerAuthenticationTest { @Test @Description("Tests authentication message successfull") - public void testSuccessfullMessageAuthentication() { + public void successfullMessageAuthentication() { final MessageProperties messageProperties = createMessageProperties(null); - final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, CONTROLLLER_ID, - FileResource.createFileResourceBySha1("12345")); - when(tenantConfigurationManagement.getConfigurationValue( + final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, null, CONTROLLER_ID, null, + FileResource.createFileResourceBySha1(SHA1)); + when(tenantConfigurationManagementMock.getConfigurationValue( eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class))) .thenReturn(CONFIG_VALUE_TRUE); - securityToken.getHeaders().put(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken " + CONTROLLLER_ID); + securityToken.putHeader(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken " + CONTROLLER_ID); final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken, messageProperties); // test - final Message onMessage = amqpMessageHandlerService.onAuthenticationRequest(message); + final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message); // verify final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage); assertThat(downloadResponse).isNotNull(); - assertThat(downloadResponse.getResponseCode()).isEqualTo(HttpStatus.NOT_FOUND.value()); + assertThat(downloadResponse.getDownloadUrl()).isNotNull(); + assertThat(downloadResponse.getResponseCode()).isEqualTo(HttpStatus.OK.value()); + assertThat(SecurityContextHolder.getContext()).isNotNull(); + assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull(); + assertThat(SecurityContextHolder.getContext().getAuthentication().getClass().getName()) + .isEqualTo(PreAuthenticatedAuthenticationToken.class.getName()); + + } + + @Test + @Description("Tests authentication message successfull with targetId intead of controllerId provided and artifactId instead of SHA1.") + public void successfullMessageAuthenticationWithTargetIdAndArtifactId() { + final MessageProperties messageProperties = createMessageProperties(null); + final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, null, null, TARGET_ID, + FileResource.createFileResourceByArtifactId(ARTIFACT_ID)); + when(tenantConfigurationManagementMock.getConfigurationValue( + eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class))) + .thenReturn(CONFIG_VALUE_TRUE); + securityToken.putHeader(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken " + CONTROLLER_ID); + final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken, + messageProperties); + + // test + final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message); + + // verify + final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage); + assertThat(downloadResponse).isNotNull(); + assertThat(downloadResponse.getDownloadUrl()).isNotNull(); + assertThat(downloadResponse.getResponseCode()).isEqualTo(HttpStatus.OK.value()); + assertThat(SecurityContextHolder.getContext()).isNotNull(); + assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull(); + assertThat(SecurityContextHolder.getContext().getAuthentication().getClass().getName()) + .isEqualTo(PreAuthenticatedAuthenticationToken.class.getName()); + + } + + @Test + @Description("Tests authentication message successfull") + public void successfullMessageAuthenticationWithTenantid() { + final MessageProperties messageProperties = createMessageProperties(null); + final TenantSecurityToken securityToken = new TenantSecurityToken(null, TENANT_ID, CONTROLLER_ID, TARGET_ID, + FileResource.createFileResourceBySha1(SHA1)); + when(tenantConfigurationManagementMock.getConfigurationValue( + eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class))) + .thenReturn(CONFIG_VALUE_TRUE); + securityToken.putHeader(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken " + CONTROLLER_ID); + final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken, + messageProperties); + + // test + final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message); + + // verify + final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage); + assertThat(downloadResponse).isNotNull(); + assertThat(downloadResponse.getDownloadUrl()).isNotNull(); + assertThat(downloadResponse.getResponseCode()).isEqualTo(HttpStatus.OK.value()); assertThat(SecurityContextHolder.getContext()).isNotNull(); assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull(); assertThat(SecurityContextHolder.getContext().getAuthentication().getClass().getName()) diff --git a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java index 62bd8e9b7..2fe2709e7 100644 --- a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java +++ b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java @@ -13,9 +13,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyObject; -import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.when; @@ -24,6 +22,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; +import org.eclipse.hawkbit.api.ArtifactUrl; import org.eclipse.hawkbit.api.ArtifactUrlHandler; import org.eclipse.hawkbit.artifact.repository.model.DbArtifact; import org.eclipse.hawkbit.dmf.amqp.api.EventTopic; @@ -31,11 +30,14 @@ import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey; import org.eclipse.hawkbit.dmf.amqp.api.MessageType; import org.eclipse.hawkbit.dmf.json.model.DownloadAndUpdateRequest; import org.eclipse.hawkbit.eventbus.event.CancelTargetAssignmentEvent; +import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.eventbus.event.TargetAssignDistributionSetEvent; import org.eclipse.hawkbit.repository.model.Artifact; 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.Target; +import org.eclipse.hawkbit.repository.model.TenantMetaData; import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTest; import org.eclipse.hawkbit.util.IpUtil; import org.junit.Test; @@ -49,6 +51,8 @@ import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.test.context.ActiveProfiles; +import com.google.common.collect.Lists; + import ru.yandex.qatools.allure.annotations.Description; import ru.yandex.qatools.allure.annotations.Features; import ru.yandex.qatools.allure.annotations.Stories; @@ -60,6 +64,7 @@ import ru.yandex.qatools.allure.annotations.Stories; public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest { private static final String TENANT = "default"; + private static final Long TENANT_ID = 4711L; private static final URI AMQP_URI = IpUtil.createAmqpUri("vHost", "mytest"); @@ -71,31 +76,47 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest { private DefaultAmqpSenderService senderService; + private SystemManagement systemManagement; + private static final String CONTROLLER_ID = "1"; + private Target testTarget; + @Override public void before() throws Exception { super.before(); + testTarget = entityFactory.generateTarget(CONTROLLER_ID, TEST_TOKEN); + testTarget.getTargetInfo().setAddress(AMQP_URI.toString()); + this.rabbitTemplate = Mockito.mock(RabbitTemplate.class); when(rabbitTemplate.getMessageConverter()).thenReturn(new Jackson2JsonMessageConverter()); senderService = Mockito.mock(DefaultAmqpSenderService.class); final ArtifactUrlHandler artifactUrlHandlerMock = Mockito.mock(ArtifactUrlHandler.class); - when(artifactUrlHandlerMock.getUrl(anyString(), anyLong(), anyString(), anyString(), anyObject())) - .thenReturn("http://mockurl"); + when(artifactUrlHandlerMock.getUrls(anyObject(), anyObject())) + .thenReturn(Lists.newArrayList(new ArtifactUrl("http", "download", "http://mockurl"))); + + systemManagement = Mockito.mock(SystemManagement.class); + final TenantMetaData tenantMetaData = Mockito.mock(TenantMetaData.class); + when(tenantMetaData.getId()).thenReturn(TENANT_ID); + when(tenantMetaData.getTenant()).thenReturn(TENANT); + + when(systemManagement.getTenantMetadata()).thenReturn(tenantMetaData); amqpMessageDispatcherService = new AmqpMessageDispatcherService(rabbitTemplate, senderService, - artifactUrlHandlerMock); + artifactUrlHandlerMock, systemSecurityContext, systemManagement); + } @Test @Description("Verfies that download and install event with no software modul works") public void testSendDownloadRequesWithEmptySoftwareModules() { final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent = new TargetAssignDistributionSetEvent( - 1L, TENANT, CONTROLLER_ID, 1L, new ArrayList(), AMQP_URI, TEST_TOKEN); + 1L, TENANT, testTarget, 1L, new ArrayList()); amqpMessageDispatcherService.targetAssignDistributionSet(targetAssignDistributionSetEvent); - final Message sendMessage = createArgumentCapture(targetAssignDistributionSetEvent.getTargetAdress()); + final Message sendMessage = createArgumentCapture( + targetAssignDistributionSetEvent.getTarget().getTargetInfo().getAddress()); final DownloadAndUpdateRequest downloadAndUpdateRequest = assertDownloadAndInstallMessage(sendMessage); assertThat(downloadAndUpdateRequest.getTargetSecurityToken()).isEqualTo(TEST_TOKEN); assertTrue("No softwaremmodule should be contained in the request", @@ -107,9 +128,10 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest { public void testSendDownloadRequesWithSoftwareModulesAndNoArtifacts() { final DistributionSet dsA = testdataFactory.createDistributionSet(""); final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent = new TargetAssignDistributionSetEvent( - 1L, TENANT, CONTROLLER_ID, 1L, dsA.getModules(), AMQP_URI, TEST_TOKEN); + 1L, TENANT, testTarget, 1L, dsA.getModules()); amqpMessageDispatcherService.targetAssignDistributionSet(targetAssignDistributionSetEvent); - final Message sendMessage = createArgumentCapture(targetAssignDistributionSetEvent.getTargetAdress()); + final Message sendMessage = createArgumentCapture( + targetAssignDistributionSetEvent.getTarget().getTargetInfo().getAddress()); final DownloadAndUpdateRequest downloadAndUpdateRequest = assertDownloadAndInstallMessage(sendMessage); assertEquals("Expecting a size of 3 software modules in the reuqest", 3, downloadAndUpdateRequest.getSoftwareModules().size()); @@ -146,9 +168,10 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest { Mockito.when(rabbitTemplate.convertSendAndReceive(any())).thenReturn(receivedList); final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent = new TargetAssignDistributionSetEvent( - 1L, TENANT, CONTROLLER_ID, 1L, dsA.getModules(), AMQP_URI, TEST_TOKEN); + 1L, TENANT, testTarget, 1L, dsA.getModules()); amqpMessageDispatcherService.targetAssignDistributionSet(targetAssignDistributionSetEvent); - final Message sendMessage = createArgumentCapture(targetAssignDistributionSetEvent.getTargetAdress()); + final Message sendMessage = createArgumentCapture( + targetAssignDistributionSetEvent.getTarget().getTargetInfo().getAddress()); final DownloadAndUpdateRequest downloadAndUpdateRequest = assertDownloadAndInstallMessage(sendMessage); assertEquals("DownloadAndUpdateRequest event should contains 3 software modules", 3, downloadAndUpdateRequest.getSoftwareModules().size()); diff --git a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java index a1591c07c..e4c309412 100644 --- a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java +++ b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java @@ -54,7 +54,6 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.TargetInfo; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.security.SecurityTokenGenerator; -import org.eclipse.hawkbit.security.SystemSecurityContext; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -81,8 +80,12 @@ import ru.yandex.qatools.allure.annotations.Stories; public class AmqpMessageHandlerServiceTest { private static final String TENANT = "DEFAULT"; + private static final Long TENANT_ID = 123L; + private static String CONTROLLLER_ID = "123"; + private static final Long TARGET_ID = 123L; private AmqpMessageHandlerService amqpMessageHandlerService; + private AmqpAuthenticationMessageHandler amqpAuthenticationMessageHandlerService; private MessageConverter messageConverter; @@ -113,22 +116,16 @@ public class AmqpMessageHandlerServiceTest { @Mock private RabbitTemplate rabbitTemplate; - @Mock - private SystemSecurityContext systemSecurityContextMock; - @Before public void before() throws Exception { messageConverter = new Jackson2JsonMessageConverter(); when(rabbitTemplate.getMessageConverter()).thenReturn(messageConverter); - amqpMessageHandlerService = new AmqpMessageHandlerService(rabbitTemplate, amqpMessageDispatcherServiceMock); - amqpMessageHandlerService.setControllerManagement(controllerManagementMock); - amqpMessageHandlerService.setAuthenticationManager(authenticationManagerMock); - amqpMessageHandlerService.setArtifactManagement(artifactManagementMock); - amqpMessageHandlerService.setCache(cacheMock); - amqpMessageHandlerService.setHostnameResolver(hostnameResolverMock); - amqpMessageHandlerService.setEntityFactory(entityFactoryMock); - amqpMessageHandlerService.setSystemSecurityContext(systemSecurityContextMock); + amqpMessageHandlerService = new AmqpMessageHandlerService(rabbitTemplate, amqpMessageDispatcherServiceMock, + controllerManagementMock, entityFactoryMock); + amqpAuthenticationMessageHandlerService = new AmqpAuthenticationMessageHandler(rabbitTemplate, + authenticationManagerMock, artifactManagementMock, cacheMock, hostnameResolverMock, + controllerManagementMock); } @Test @@ -279,13 +276,13 @@ public class AmqpMessageHandlerServiceTest { @Description("Tests that an download request is denied for an artifact which does not exists") public void authenticationRequestDeniedForArtifactWhichDoesNotExists() { final MessageProperties messageProperties = createMessageProperties(null); - final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, "123", + final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, TENANT_ID, CONTROLLLER_ID, TARGET_ID, FileResource.createFileResourceBySha1("12345")); final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken, messageProperties); // test - final Message onMessage = amqpMessageHandlerService.onAuthenticationRequest(message); + final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message); // verify final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage); @@ -298,7 +295,7 @@ public class AmqpMessageHandlerServiceTest { @Description("Tests that an download request is denied for an artifact which is not assigned to the requested target") public void authenticationRequestDeniedForArtifactWhichIsNotAssignedToTarget() { final MessageProperties messageProperties = createMessageProperties(null); - final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, "123", + final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, TENANT_ID, CONTROLLLER_ID, TARGET_ID, FileResource.createFileResourceBySha1("12345")); final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken, messageProperties); @@ -309,7 +306,7 @@ public class AmqpMessageHandlerServiceTest { .thenThrow(EntityNotFoundException.class); // test - final Message onMessage = amqpMessageHandlerService.onAuthenticationRequest(message); + final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message); // verify final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage); @@ -322,7 +319,7 @@ public class AmqpMessageHandlerServiceTest { @Description("Tests that an download request is allowed for an artifact which exists and assigned to the requested target") public void authenticationRequestAllowedForArtifactWhichExistsAndAssignedToTarget() throws MalformedURLException { final MessageProperties messageProperties = createMessageProperties(null); - final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, "123", + final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, TENANT_ID, CONTROLLLER_ID, TARGET_ID, FileResource.createFileResourceBySha1("12345")); final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken, messageProperties); @@ -340,7 +337,7 @@ public class AmqpMessageHandlerServiceTest { when(hostnameResolverMock.resolveHostname()).thenReturn(new URL("http://localhost")); // test - final Message onMessage = amqpMessageHandlerService.onAuthenticationRequest(message); + final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message); // verify final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage); @@ -364,15 +361,12 @@ public class AmqpMessageHandlerServiceTest { when(controllerManagementMock.addUpdateActionStatus(Matchers.any())).thenReturn(action); when(entityFactoryMock.generateActionStatus()).thenReturn(new JpaActionStatus()); // for the test the same action can be used - when(controllerManagementMock.findOldestActiveActionByTarget(Matchers.any())) - .thenReturn(Optional.of(action)); + when(controllerManagementMock.findOldestActiveActionByTarget(Matchers.any())).thenReturn(Optional.of(action)); final List softwareModuleList = createSoftwareModuleList(); when(controllerManagementMock.findSoftwareModulesByDistributionSet(Matchers.any())) .thenReturn(softwareModuleList); - when(systemSecurityContextMock.runAsSystem(anyObject())).thenReturn("securityToken"); - final MessageProperties messageProperties = createMessageProperties(MessageType.EVENT); messageProperties.setHeader(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name()); final ActionUpdateStatus actionUpdateStatus = createActionUpdateStatus(ActionStatus.FINISHED, 23L); @@ -393,10 +387,10 @@ public class AmqpMessageHandlerServiceTest { final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent = captorTargetAssignDistributionSetEvent .getValue(); - assertThat(targetAssignDistributionSetEvent.getControllerId()).as("event has wrong controller id") + assertThat(targetAssignDistributionSetEvent.getTarget().getControllerId()).as("event has wrong controller id") .isEqualTo("target1"); - assertThat(targetAssignDistributionSetEvent.getTargetToken()).as("targetoken not filled correctly") - .isEqualTo(action.getTarget().getSecurityToken()); + assertThat(targetAssignDistributionSetEvent.getTarget().getSecurityToken()) + .as("targetoken not filled correctly").isEqualTo(action.getTarget().getSecurityToken()); assertThat(targetAssignDistributionSetEvent.getActionId()).as("event has wrong action id").isEqualTo(22L); assertThat(targetAssignDistributionSetEvent.getSoftwareModules()).as("event has wrong sofware modules") .isEqualTo(softwareModuleList); diff --git a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/BaseAmqpServiceTest.java b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/BaseAmqpServiceTest.java index 20d03bdd6..582bcf130 100644 --- a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/BaseAmqpServiceTest.java +++ b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/BaseAmqpServiceTest.java @@ -52,8 +52,8 @@ public class BaseAmqpServiceTest { final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus(); actionUpdateStatus.setActionId(1L); actionUpdateStatus.setSoftwareModuleId(2L); - actionUpdateStatus.getMessage().add("Message 1"); - actionUpdateStatus.getMessage().add("Message 2"); + actionUpdateStatus.addMessage("Message 1"); + actionUpdateStatus.addMessage("Message 2"); final Message message = rabbitTemplate.getMessageConverter().toMessage(actionUpdateStatus, new MessageProperties()); diff --git a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/util/PropertyBasedArtifactUrlHandlerTest.java b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/util/PropertyBasedArtifactUrlHandlerTest.java deleted file mode 100644 index ea01bc0ec..000000000 --- a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/util/PropertyBasedArtifactUrlHandlerTest.java +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -package org.eclipse.hawkbit.util; - -import static org.junit.Assert.assertEquals; - -import org.eclipse.hawkbit.AmqpTestConfiguration; -import org.eclipse.hawkbit.api.ArtifactUrlHandler; -import org.eclipse.hawkbit.api.UrlProtocol; -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.test.util.AbstractIntegrationTest; -import org.junit.Before; -import org.junit.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.SpringApplicationConfiguration; - -import ru.yandex.qatools.allure.annotations.Description; -import ru.yandex.qatools.allure.annotations.Features; -import ru.yandex.qatools.allure.annotations.Stories; - -/** - * Tests for creating urls to download artifacts. - */ -@Features("Component Tests - Artifact URL Handler") -@Stories("Test to generate the artifact download URL") -@SpringApplicationConfiguration(classes = { AmqpTestConfiguration.class, - org.eclipse.hawkbit.RepositoryApplicationConfiguration.class }) -public class PropertyBasedArtifactUrlHandlerTest extends AbstractIntegrationTest { - - private static final String HTTPS_LOCALHOST = "https://localhost/"; - private static final String HTTP_LOCALHOST = "http://localhost/"; - - @Autowired - private ArtifactUrlHandler urlHandlerProperties; - - private LocalArtifact localArtifact; - private static final String CONTROLLER_ID = "Test"; - private String fileName; - private Long softwareModuleId; - private String sha1Hash; - - @Before - public void setup() { - final DistributionSet dsA = testdataFactory.createDistributionSet(""); - final SoftwareModule module = dsA.getModules().iterator().next(); - localArtifact = testdataFactory.createLocalArtifacts(module.getId()).stream().findAny().get(); - softwareModuleId = localArtifact.getSoftwareModule().getId(); - fileName = localArtifact.getFilename(); - sha1Hash = localArtifact.getSha1Hash(); - - } - - @Test - @Description("Tests the generation of http download url.") - public void testHttpUrl() { - - final String url = urlHandlerProperties.getUrl(CONTROLLER_ID, softwareModuleId, fileName, sha1Hash, - UrlProtocol.HTTP); - assertEquals("http is build incorrect", - HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + CONTROLLER_ID - + "/softwaremodules/" + localArtifact.getSoftwareModule().getId() + "/artifacts/" - + localArtifact.getFilename(), - url); - } - - @Test - @Description("Tests the generation of https download url.") - public void testHttpsUrl() { - final String url = urlHandlerProperties.getUrl(CONTROLLER_ID, softwareModuleId, fileName, sha1Hash, - UrlProtocol.HTTPS); - assertEquals("https is build incorrect", - HTTPS_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + CONTROLLER_ID - + "/softwaremodules/" + localArtifact.getSoftwareModule().getId() + "/artifacts/" - + localArtifact.getFilename(), - url); - } - - @Test - @Description("Tests the generation of coap download url.") - public void testCoapUrl() { - final String url = urlHandlerProperties.getUrl(CONTROLLER_ID, softwareModuleId, fileName, sha1Hash, - UrlProtocol.COAP); - - assertEquals("coap is build incorrect", "coap://127.0.0.1:5683/fw/" + tenantAware.getCurrentTenant() + "/" - + CONTROLLER_ID + "/sha1/" + localArtifact.getSha1Hash(), url); - } -} diff --git a/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/ActionUpdateStatus.java b/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/ActionUpdateStatus.java index ac0926080..2262cab96 100644 --- a/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/ActionUpdateStatus.java +++ b/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/ActionUpdateStatus.java @@ -9,6 +9,7 @@ package org.eclipse.hawkbit.dmf.json.model; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @@ -18,9 +19,6 @@ import com.fasterxml.jackson.annotation.JsonProperty; /** * JSON representation of action update status. - * - * - * */ @JsonInclude(Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) @@ -32,7 +30,7 @@ public class ActionUpdateStatus { @JsonProperty(required = true) private ActionStatus actionStatus; @JsonProperty - private final List message = new ArrayList<>(); + private List message; public Long getActionId() { return actionId; @@ -59,7 +57,19 @@ public class ActionUpdateStatus { } public List getMessage() { - return message; + if (message == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(message); + } + + public boolean addMessage(final String message) { + if (this.message == null) { + this.message = new ArrayList<>(); + } + + return this.message.add(message); } } diff --git a/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/Artifact.java b/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/Artifact.java index 27da75fb0..8375663b8 100644 --- a/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/Artifact.java +++ b/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/Artifact.java @@ -8,7 +8,7 @@ */ package org.eclipse.hawkbit.dmf.json.model; -import java.util.EnumMap; +import java.util.Collections; import java.util.Map; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @@ -25,15 +25,6 @@ import com.fasterxml.jackson.annotation.JsonProperty; @JsonInclude(Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) public class Artifact { - - /** - * Represented the supported protocols for artifact url's. - * - */ - public enum UrlProtocol { - COAP, HTTP, HTTPS - } - @JsonProperty private String filename; @@ -44,13 +35,17 @@ public class Artifact { private Long size; @JsonProperty - private Map urls = new EnumMap<>(UrlProtocol.class); + private Map urls; - public Map getUrls() { - return urls; + public Map getUrls() { + if (urls == null) { + return Collections.emptyMap(); + } + + return Collections.unmodifiableMap(urls); } - public void setUrls(final Map urls) { + public void setUrls(final Map urls) { this.urls = urls; } diff --git a/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DownloadAndUpdateRequest.java b/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DownloadAndUpdateRequest.java index 88cb80975..8664f639e 100644 --- a/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DownloadAndUpdateRequest.java +++ b/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DownloadAndUpdateRequest.java @@ -8,7 +8,8 @@ */ package org.eclipse.hawkbit.dmf.json.model; -import java.util.LinkedList; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @@ -30,7 +31,7 @@ public class DownloadAndUpdateRequest { private String targetSecurityToken; @JsonProperty - private final List softwareModules = new LinkedList<>(); + private List softwareModules; public Long getActionId() { return actionId; @@ -49,7 +50,11 @@ public class DownloadAndUpdateRequest { } public List getSoftwareModules() { - return softwareModules; + if (softwareModules == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(softwareModules); } /** @@ -59,6 +64,10 @@ public class DownloadAndUpdateRequest { * the module */ public void addSoftwareModule(final SoftwareModule createSoftwareModule) { + if (softwareModules == null) { + softwareModules = new ArrayList<>(); + } + softwareModules.add(createSoftwareModule); } diff --git a/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/SoftwareModule.java b/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/SoftwareModule.java index 193f33575..70a7880d8 100644 --- a/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/SoftwareModule.java +++ b/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/SoftwareModule.java @@ -8,7 +8,7 @@ */ package org.eclipse.hawkbit.dmf.json.model; -import java.util.LinkedList; +import java.util.Collections; import java.util.List; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -35,7 +35,7 @@ public class SoftwareModule { @JsonProperty private String moduleVersion; @JsonProperty - private List artifacts = new LinkedList<>(); + private List artifacts; public String getModuleType() { return moduleType; @@ -54,7 +54,11 @@ public class SoftwareModule { } public List getArtifacts() { - return artifacts; + if (artifacts == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(artifacts); } public Long getModuleId() { diff --git a/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/TenantSecurityToken.java b/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/TenantSecurityToken.java index 19293f3eb..d2248fed8 100644 --- a/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/TenantSecurityToken.java +++ b/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/TenantSecurityToken.java @@ -8,6 +8,7 @@ */ package org.eclipse.hawkbit.dmf.json.model; +import java.util.Collections; import java.util.Map; import java.util.TreeMap; @@ -27,16 +28,47 @@ public class TenantSecurityToken { public static final String AUTHORIZATION_HEADER = "Authorization"; - @JsonProperty - private final String tenant; - @JsonProperty + @JsonProperty(required = false) + private String tenant; + @JsonProperty(required = false) + private final Long tenantId; + @JsonProperty(required = false) private final String controllerId; @JsonProperty(required = false) - private Map headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + private final Long targetId; + + @JsonProperty(required = false) + private Map headers; @JsonProperty(required = false) private final FileResource fileResource; + /** + * Constructor. + * + * @param tenant + * the tenant for the security token + * @param tenantId + * alternative tenant identification by technical ID + * @param controllerId + * the ID of the controller for the security token + * @param targetId + * alternative target identification by technical ID + * @param fileResource + * the file to obtain + */ + @JsonCreator + public TenantSecurityToken(@JsonProperty("tenant") final String tenant, + @JsonProperty("tenantId") final Long tenantId, @JsonProperty("controllerId") final String controllerId, + @JsonProperty("targetId") final Long targetId, + @JsonProperty("fileResource") final FileResource fileResource) { + this.tenant = tenant; + this.tenantId = tenantId; + this.controllerId = controllerId; + this.targetId = targetId; + this.fileResource = fileResource; + } + /** * Constructor. * @@ -47,13 +79,26 @@ public class TenantSecurityToken { * @param fileResource * the file to obtain */ - @JsonCreator - public TenantSecurityToken(@JsonProperty("tenant") final String tenant, - @JsonProperty("controllerId") final String controllerId, - @JsonProperty("fileResource") final FileResource fileResource) { + public TenantSecurityToken(final String tenant, final String controllerId, final FileResource fileResource) { + this(tenant, null, controllerId, null, fileResource); + } + + /** + * Constructor. + * + * @param tenantId + * the tenant for the security token + * @param targetId + * target identification by technical ID + * @param fileResource + * the file to obtain + */ + public TenantSecurityToken(final Long tenantId, final Long targetId, final FileResource fileResource) { + this(null, tenantId, null, targetId, fileResource); + } + + public void setTenant(final String tenant) { this.tenant = tenant; - this.controllerId = controllerId; - this.fileResource = fileResource; } public String getTenant() { @@ -65,13 +110,25 @@ public class TenantSecurityToken { } public Map getHeaders() { - return headers; + if (headers == null) { + return Collections.emptyMap(); + } + + return Collections.unmodifiableMap(headers); } public FileResource getFileResource() { return fileResource; } + public Long getTenantId() { + return tenantId; + } + + public Long getTargetId() { + return targetId; + } + /** * Gets a header value. * @@ -80,6 +137,10 @@ public class TenantSecurityToken { * @return the value */ public String getHeader(final String name) { + if (headers == null) { + return null; + } + return headers.get(name); } @@ -88,6 +149,24 @@ public class TenantSecurityToken { this.headers.putAll(headers); } + /** + * Associates the specified header value with the specified name. + * + * @param name + * of the header + * @param value + * of the header + * + * @return the previous value associated with the name, or + * null if there was no mapping for name. + */ + public String putHeader(final String name, final String value) { + if (headers == null) { + headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + } + return headers.put(name, value); + } + /** * File resource descriptor which is used to ask for the resource to * download e.g. The lookup of the file can be different e.g. by SHA1 hash @@ -99,6 +178,8 @@ public class TenantSecurityToken { @JsonProperty(required = false) private String sha1; @JsonProperty(required = false) + private Long artifactId; + @JsonProperty(required = false) private String filename; @JsonProperty(required = false) private SoftwareModuleFilenameResource softwareModuleFilenameResource; @@ -128,6 +209,14 @@ public class TenantSecurityToken { this.softwareModuleFilenameResource = softwareModuleFilenameResource; } + public Long getArtifactId() { + return artifactId; + } + + public void setArtifactId(final Long artifactId) { + this.artifactId = artifactId; + } + /** * factory method to create a file resource for an SHA1 lookup. * @@ -141,6 +230,19 @@ public class TenantSecurityToken { return resource; } + /** + * factory method to create a file resource for an artifact ID lookup. + * + * @param artifactId + * the artifact IF key of the file to obtain + * @return the {@link FileResource} with SHA1 key set + */ + public static FileResource createFileResourceByArtifactId(final Long artifactId) { + final FileResource resource = new FileResource(); + resource.artifactId = artifactId; + return resource; + } + /** * factory method to create a file resource for an filename lookup. * @@ -173,7 +275,7 @@ public class TenantSecurityToken { @Override public String toString() { - return "FileResource [sha1=" + sha1 + ", filename=" + filename + "]"; + return "FileResource [sha1=" + sha1 + ", artifactId=" + artifactId + ", filename=" + filename + "]"; } /** diff --git a/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/AbstractHttpControllerAuthenticationFilter.java b/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/AbstractHttpControllerAuthenticationFilter.java index 2f2726d98..f62307789 100644 --- a/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/AbstractHttpControllerAuthenticationFilter.java +++ b/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/AbstractHttpControllerAuthenticationFilter.java @@ -168,10 +168,10 @@ public abstract class AbstractHttpControllerAuthenticationFilter extends Abstrac private TenantSecurityToken createTenantSecruityTokenVariables(final HttpServletRequest request, final String tenant, final String controllerId) { - final TenantSecurityToken secruityToken = new TenantSecurityToken(tenant, controllerId, + final TenantSecurityToken secruityToken = new TenantSecurityToken(tenant, null, controllerId, null, FileResource.createFileResourceBySha1("")); final UnmodifiableIterator forEnumeration = Iterators.forEnumeration(request.getHeaderNames()); - forEnumeration.forEachRemaining(header -> secruityToken.getHeaders().put(header, request.getHeader(header))); + forEnumeration.forEachRemaining(header -> secruityToken.putHeader(header, request.getHeader(header))); return secruityToken; } diff --git a/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/PagedList.java b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/PagedList.java index 173f3ce31..534cfa98d 100644 --- a/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/PagedList.java +++ b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/PagedList.java @@ -8,6 +8,7 @@ */ package org.eclipse.hawkbit.mgmt.json.model; +import java.util.Collections; import java.util.List; import javax.validation.constraints.NotNull; @@ -72,7 +73,7 @@ public class PagedList extends ResourceSupport { } public List getContent() { - return content; + return Collections.unmodifiableList(content); } } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ArtifactManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ArtifactManagement.java index 167b6019f..b78d1500d 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ArtifactManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ArtifactManagement.java @@ -208,15 +208,16 @@ public interface ArtifactManagement { void deleteLocalArtifact(@NotNull Long id); /** - * Searches for {@link Artifact} with given {@link Identifiable}. + * Searches for {@link LocalArtifact} with given {@link Identifiable}. * * @param id * to search for - * @return found {@link Artifact} or null is it could not be - * found. + * @return found {@link LocalArtifact} or null is it could not + * be found. */ - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - Artifact findArtifact(@NotNull Long id); + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR + + SpringEvalExpressions.IS_CONTROLLER) + LocalArtifact findLocalArtifact(@NotNull Long id); /** * Find by artifact by software module id and filename. diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java index f93b42e48..58d26439d 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java @@ -183,22 +183,6 @@ public interface ControllerManagement { @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) String getPollingTime(); - /** - * 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 - */ - @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER + SpringEvalExpressions.HAS_AUTH_OR - + SpringEvalExpressions.HAS_AUTH_READ_TARGET_SEC_TOKEN) - String getSecurityTokenByControllerId(@NotEmpty String controllerId); - /** * Checks if a given target has currently or has even been assigned to the * given artifact through the action history list. This can e.g. indicate if @@ -218,6 +202,25 @@ public interface ControllerManagement { @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) boolean hasTargetArtifactAssigned(@NotNull String controllerId, @NotNull LocalArtifact localArtifact); + /** + * Checks if a given target has currently or has even been assigned to the + * given artifact through the action history list. This can e.g. indicate if + * a target is allowed to download a given artifact because it has currently + * assigned or had ever been assigned to the target and so it's visible to a + * specific target e.g. for downloading. + * + * @param targetId + * the ID of the target to check + * @param localArtifact + * the artifact to verify if the given target had even been + * assigned to + * @return {@code true} if the given target has currently or had ever a + * relation to the given artifact through the action history, + * otherwise {@code false} + */ + @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) + boolean hasTargetArtifactAssigned(@NotNull Long targetId, @NotNull LocalArtifact localArtifact); + /** * Registers retrieved status for given {@link Target} and {@link Action} if * it does not exist yet. @@ -300,4 +303,32 @@ public interface ControllerManagement { TargetInfo updateTargetStatus(@NotNull TargetInfo targetInfo, TargetUpdateStatus status, Long lastTargetQuery, URI address); + /** + * Finds {@link Target} based on given controller ID returns found Target + * without details, i.e. NO {@link Target#getTags()} and + * {@link Target#getActions()} possible. + * + * @param controllerId + * to look for. + * @return {@link Target} or {@code null} if it does not exist + * @see Target#getControllerId() + */ + @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER + SpringEvalExpressions.HAS_AUTH_OR + + SpringEvalExpressions.IS_SYSTEM_CODE) + Target findByControllerId(@NotEmpty final String controllerId); + + /** + * Finds {@link Target} based on given ID returns found Target without + * details, i.e. NO {@link Target#getTags()} and {@link Target#getActions()} + * possible. + * + * @param targetId + * to look for. + * @return {@link Target} or {@code null} if it does not exist + * @see Target#getId() + */ + @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER + SpringEvalExpressions.HAS_AUTH_OR + + SpringEvalExpressions.IS_SYSTEM_CODE) + Target findByTargetId(final long targetId); + } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DistributionSetAssignmentResult.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DistributionSetAssignmentResult.java index 704bb38e9..171808cb0 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DistributionSetAssignmentResult.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DistributionSetAssignmentResult.java @@ -57,7 +57,11 @@ public class DistributionSetAssignmentResult extends AssignmentResult { * @return the actionIds */ public List getActions() { - return actions; + if (actions == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(actions); } @Override diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DistributionSetManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DistributionSetManagement.java index d8a6f54fe..1de51fad9 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DistributionSetManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DistributionSetManagement.java @@ -205,8 +205,10 @@ public interface DistributionSetManagement { /** * deletes a distribution set meta data entry. * - * @param id - * the ID of the distribution set meta data to delete + * @param distributionSet + * where meta data has to be deleted + * @param key + * of the meta data element */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) void deleteDistributionSetMetadata(@NotNull final DistributionSet distributionSet, @NotNull final String key); @@ -429,7 +431,7 @@ public interface DistributionSetManagement { */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - DistributionSetType findDistributionSetTypeByKey(@NotNull String key); + DistributionSetType findDistributionSetTypeByKey(@NotEmpty String key); /** * @param name @@ -469,15 +471,16 @@ public interface DistributionSetManagement { /** * finds a single distribution set meta data by its id. * - * @param id - * the id of the distribution set meta data containing the meta - * data key and the ID of the distribution set + * @param distributionSet + * where meta data has to rind + * @param key + * of the meta data element * @return the found DistributionSetMetadata 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) - DistributionSetMetadata findOne(@NotNull DistributionSet distributionSet, @NotNull String key); + DistributionSetMetadata findOne(@NotNull DistributionSet distributionSet, @NotEmpty String key); /** * Checks if a {@link DistributionSet} is currently in use by a target in diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SoftwareManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SoftwareManagement.java index dfe05100e..2746686a7 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SoftwareManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SoftwareManagement.java @@ -155,11 +155,13 @@ public interface SoftwareManagement { /** * deletes a software module meta data entry. * - * @param id - * the ID of the software module meta data to delete + * @param softwareModule + * where meta data has to be deleted + * @param key + * of the metda data element */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) - void deleteSoftwareModuleMetadata(@NotNull SoftwareModule softwareModule, @NotNull String key); + void deleteSoftwareModuleMetadata(@NotNull SoftwareModule softwareModule, @NotEmpty String key); /** * Deletes {@link SoftwareModule}s which is any if the given ids. @@ -251,9 +253,10 @@ public interface SoftwareManagement { /** * 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 + * @param softwareModule + * where meta data has to be found + * @param key + * of the meta data element * @return the found SoftwareModuleMetadata or {@code null} if not exits * @throws EntityNotFoundException * in case the meta data does not exists for the given key @@ -280,8 +283,8 @@ public interface SoftwareManagement { * * @param softwareModuleId * the software module id to retrieve the meta data from - * @param spec - * the specification to filter the result + * @param rsqlParam + * filter definition in RSQL syntax * @param pageable * the page request to page the result * @return a paged result of all meta data entries for a given software @@ -346,8 +349,8 @@ public interface SoftwareManagement { /** * Retrieves all {@link SoftwareModule}s with a given specification. * - * @param spec - * the specification to filter the software modules + * @param rsqlParam + * filter definition in RSQL syntax * @param pageable * pagination parameter * @return the found {@link SoftwareModule}s @@ -392,7 +395,7 @@ public interface SoftwareManagement { * {@link SoftwareModuleType#getKey()} */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - SoftwareModuleType findSoftwareModuleTypeByKey(@NotNull String key); + SoftwareModuleType findSoftwareModuleTypeByKey(@NotEmpty String key); /** * @@ -415,8 +418,8 @@ public interface SoftwareManagement { /** * Retrieves all {@link SoftwareModuleType}s with a given specification. * - * @param spec - * the specification to filter the software modules types + * @param rsqlParam + * filter definition in RSQL syntax * @param pageable * pagination parameter * @return the found {@link SoftwareModuleType}s diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SystemManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SystemManagement.java index cd414b09a..131f63107 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SystemManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SystemManagement.java @@ -63,7 +63,8 @@ public interface SystemManagement { */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR + SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR - + SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION) + + SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION + SpringEvalExpressions.HAS_AUTH_OR + + SpringEvalExpressions.IS_CONTROLLER) TenantMetaData getTenantMetadata(); /** @@ -93,4 +94,14 @@ public interface SystemManagement { @PreAuthorize(SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION) TenantMetaData updateTenantMetadata(@NotNull TenantMetaData metaData); + /** + * Returns {@link TenantMetaData} of given tenant ID. + * + * @param tenantId + * to retrieve data for + * @return {@link TenantMetaData} of given tenant + */ + @PreAuthorize(SpringEvalExpressions.IS_SYSTEM_CODE) + TenantMetaData getTenantMetadata(@NotNull Long tenantId); + } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/eventbus/event/RolloutGroupCreatedEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/eventbus/event/RolloutGroupCreatedEvent.java index 41f08c91d..47fb6b39e 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/eventbus/event/RolloutGroupCreatedEvent.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/eventbus/event/RolloutGroupCreatedEvent.java @@ -34,6 +34,8 @@ public class RolloutGroupCreatedEvent extends AbstractDistributedEvent { * the revision of the event * @param rolloutId * the ID of the rollout the group has been created + * @param rolloutGroupId + * identifier of this group * @param totalRolloutGroup * the total number of rollout groups for this rollout * @param createdRolloutGroup diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/eventbus/event/TargetAssignDistributionSetEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/eventbus/event/TargetAssignDistributionSetEvent.java index 586a4b9d2..e87d49b57 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/eventbus/event/TargetAssignDistributionSetEvent.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/eventbus/event/TargetAssignDistributionSetEvent.java @@ -8,11 +8,11 @@ */ package org.eclipse.hawkbit.repository.eventbus.event; -import java.net.URI; import java.util.Collection; import org.eclipse.hawkbit.eventbus.event.DefaultEvent; import org.eclipse.hawkbit.repository.model.SoftwareModule; +import org.eclipse.hawkbit.repository.model.Target; /** * Event that gets sent when a distribution set gets assigned to a target. @@ -21,10 +21,8 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule; public class TargetAssignDistributionSetEvent extends DefaultEvent { private final Collection softwareModules; - private final String controllerId; + private final Target target; private final Long actionId; - private final URI targetAdress; - private final String targetToken; /** * Creates a new {@link TargetAssignDistributionSetEvent}. @@ -33,26 +31,19 @@ public class TargetAssignDistributionSetEvent extends DefaultEvent { * the revision of the event * @param tenant * the tenant of the event - * @param controllerId - * the ID of the controller + * @param target + * the assigned {@link Target} * @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 - * @param targetToken - * the authentication token of the target */ - public TargetAssignDistributionSetEvent(final long revision, final String tenant, final String controllerId, - final Long actionId, final Collection softwareModules, final URI targetAdress, - final String targetToken) { + public TargetAssignDistributionSetEvent(final long revision, final String tenant, final Target target, + final Long actionId, final Collection softwareModules) { super(revision, tenant); - this.controllerId = controllerId; + this.target = target; this.actionId = actionId; this.softwareModules = softwareModules; - this.targetAdress = targetAdress; - this.targetToken = targetToken; } /** @@ -63,11 +54,11 @@ public class TargetAssignDistributionSetEvent extends DefaultEvent { } /** - * @return the controllerId of the Target which has been assigned to the - * distribution set + * @return the {@link Target} which has been assigned to the distribution + * set */ - public String getControllerId() { - return controllerId; + public Target getTarget() { + return target; } /** @@ -76,12 +67,4 @@ public class TargetAssignDistributionSetEvent extends DefaultEvent { public Collection getSoftwareModules() { return softwareModules; } - - public URI getTargetAdress() { - return targetAdress; - } - - public String getTargetToken() { - return targetToken; - } } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/AssignedSoftwareModule.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/AssignedSoftwareModule.java index b23b3f0fe..9215fe46f 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/AssignedSoftwareModule.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/AssignedSoftwareModule.java @@ -60,7 +60,7 @@ public class AssignedSoftwareModule implements Serializable { final int prime = 31; int result = 1; result = prime * result + (assigned ? 1231 : 1237); - result = prime * result + (softwareModule == null ? 0 : softwareModule.hashCode()); + result = prime * result + ((softwareModule == null) ? 0 : softwareModule.hashCode()); return result; } @@ -72,7 +72,7 @@ public class AssignedSoftwareModule implements Serializable { if (obj == null) { return false; } - if (!(obj instanceof AssignedSoftwareModule)) { + if (getClass() != obj.getClass()) { return false; } final AssignedSoftwareModule other = (AssignedSoftwareModule) obj; diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/AssignmentResult.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/AssignmentResult.java index 9468c8a88..6333a61fc 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/AssignmentResult.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/AssignmentResult.java @@ -8,6 +8,7 @@ */ package org.eclipse.hawkbit.repository.model; +import java.util.Collections; import java.util.List; /** @@ -82,14 +83,22 @@ public class AssignmentResult { * @return {@link List} of assigned entity. */ public List getAssignedEntity() { - return assignedEntity; + if (assignedEntity == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(assignedEntity); } /** * @return {@link List} of unassigned entity. */ public List getUnassignedEntity() { - return unassignedEntity; + if (unassignedEntity == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(unassignedEntity); } } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/EntityInterceptor.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/EntityInterceptor.java index b6fff3f91..9ed069c66 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/EntityInterceptor.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/EntityInterceptor.java @@ -11,6 +11,9 @@ package org.eclipse.hawkbit.repository.model; /** * Interface for the entity interceptor lifecycle. */ +// Exception squid:EmptyStatementUsageCheck - don't want to force users to +// impelemnt all methods +@SuppressWarnings("squid:EmptyStatementUsageCheck") public interface EntityInterceptor { /** diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaArtifactManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaArtifactManagement.java index 3c94d885b..5e16b7cd9 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaArtifactManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaArtifactManagement.java @@ -29,7 +29,6 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaExternalArtifactProvider; import org.eclipse.hawkbit.repository.jpa.model.JpaLocalArtifact; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule; import org.eclipse.hawkbit.repository.jpa.specifications.SoftwareModuleSpecification; -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; @@ -194,7 +193,7 @@ public class JpaArtifactManagement implements ArtifactManagement { } @Override - public Artifact findArtifact(final Long id) { + public LocalArtifact findLocalArtifact(final Long id) { return localArtifactRepository.findOne(id); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java index 03ace2e1f..a7da485ad 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java @@ -158,6 +158,15 @@ public class JpaControllerManagement implements ControllerManagement { return actionRepository.count(ActionSpecifications.hasTargetAssignedArtifact(target, localArtifact)) > 0; } + @Override + public boolean hasTargetArtifactAssigned(final Long targetId, final LocalArtifact localArtifact) { + final Target target = targetRepository.findOne(targetId); + if (target == null) { + return false; + } + return actionRepository.count(ActionSpecifications.hasTargetAssignedArtifact(target, localArtifact)) > 0; + } + @Override public List findActiveActionByTarget(final Target target) { return actionRepository.findByTargetAndActiveOrderByIdAsc((JpaTarget) target, true); @@ -456,12 +465,6 @@ public class JpaControllerManagement implements ControllerManagement { return actionStatusRepository.save((JpaActionStatus) statusMessage); } - @Override - public String getSecurityTokenByControllerId(final String controllerId) { - final Target target = targetRepository.findByControllerId(controllerId); - return target != null ? target.getSecurityToken() : null; - } - @Override @Modifying @Transactional(isolation = Isolation.READ_UNCOMMITTED) @@ -475,4 +478,14 @@ public class JpaControllerManagement implements ControllerManagement { cacheWriteNotify.downloadProgress(statusId, requestedBytes, shippedBytesSinceLast, shippedBytesOverall); } + @Override + public Target findByControllerId(final String controllerId) { + return targetRepository.findByControllerId(controllerId); + } + + @Override + public Target findByTargetId(final long targetId) { + return targetRepository.findOne(targetId); + } + } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java index 09c308a3b..f97a2d178 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java @@ -352,14 +352,13 @@ public class JpaDeploymentManagement implements DeploymentManagement { private void assignDistributionSetEvent(final JpaTarget target, final Long actionId, final List modules) { ((JpaTargetInfo) target.getTargetInfo()).setUpdateStatus(TargetUpdateStatus.PENDING); - final String targetSecurityToken = systemSecurityContext.runAsSystem(() -> target.getSecurityToken()); + @SuppressWarnings({ "unchecked", "rawtypes" }) final Collection softwareModules = (Collection) modules; afterCommit.afterCommit(() -> { eventBus.post(new TargetInfoUpdateEvent(target.getTargetInfo())); - eventBus.post(new TargetAssignDistributionSetEvent(target.getOptLockRevision(), target.getTenant(), - target.getControllerId(), actionId, softwareModules, target.getTargetInfo().getAddress(), - targetSecurityToken)); + eventBus.post(new TargetAssignDistributionSetEvent(target.getOptLockRevision(), target.getTenant(), target, + actionId, softwareModules)); }); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSystemManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSystemManagement.java index 3b7f77b47..ca7eb4b3c 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSystemManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSystemManagement.java @@ -299,4 +299,9 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst Constants.DST_DEFAULT_OS_WITH_APPS_NAME, "Default type with Firmware/OS and optional app(s).") .addMandatoryModuleType(os).addOptionalModuleType(app)); } + + @Override + public TenantMetaData getTenantMetadata(final Long tenantId) { + return tenantMetaDataRepository.findOne(tenantId); + } } \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTagManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTagManagement.java index fb4b1999f..f81415722 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTagManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTagManagement.java @@ -125,7 +125,7 @@ public class JpaTagManagement implements TagManagement { final List changed = new LinkedList<>(); for (final JpaTarget target : targetRepository.findByTag(tag)) { - target.getTags().remove(tag); + target.removeTag(tag); changed.add(target); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSetTag.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSetTag.java index 84818685f..dee17a80e 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSetTag.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSetTag.java @@ -75,24 +75,4 @@ public class JpaDistributionSetTag extends AbstractJpaTag implements Distributio return Collections.unmodifiableList(assignedToDistributionSet); } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + this.getClass().getName().hashCode(); - return result; - } - - @Override - public boolean equals(final Object obj) { // NOSONAR - as this is generated - if (!super.equals(obj)) { - return false; - } - if (!(obj instanceof DistributionSetTag)) { - return false; - } - - return true; - } } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTarget.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTarget.java index 31bb5a97e..191238c88 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTarget.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTarget.java @@ -162,7 +162,7 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable getRolloutTargetGroup() { @@ -210,7 +210,7 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable(4); + actions = new ArrayList<>(); } return actions.add(action); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetTag.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetTag.java index a04933b6d..a77d4b42d 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetTag.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetTag.java @@ -73,24 +73,4 @@ public class JpaTargetTag extends AbstractJpaTag implements TargetTag { return Collections.unmodifiableList(assignedToTargets); } - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + this.getClass().getName().hashCode(); - return result; - } - - @Override - public boolean equals(final Object obj) { - if (!super.equals(obj)) { - return false; - } - if (!(obj instanceof TargetTag)) { - return false; - } - - return true; - } - } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ArtifactManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ArtifactManagementTest.java index ac12ff4f9..cb117ad7a 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ArtifactManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ArtifactManagementTest.java @@ -279,23 +279,23 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTestWithMongoD /** * Test method for - * {@link org.eclipse.hawkbit.repository.ArtifactManagement#findArtifact(java.lang.Long)} + * {@link org.eclipse.hawkbit.repository.ArtifactManagement#findLocalArtifact(java.lang.Long)} * . * * @throws IOException * @throws NoSuchAlgorithmException */ @Test - @Description("Loads an artifact based on given ID.") - public void findArtifact() throws NoSuchAlgorithmException, IOException { + @Description("Loads an local artifact based on given ID.") + public void findLocalArtifact() throws NoSuchAlgorithmException, IOException { SoftwareModule sm = new JpaSoftwareModule(softwareManagement.findSoftwareModuleTypeByKey("os"), "name 1", "version 1", null, null); sm = softwareManagement.createSoftwareModule(sm); - final Artifact result = artifactManagement.createLocalArtifact(new RandomGeneratedInputStream(5 * 1024), + final LocalArtifact result = artifactManagement.createLocalArtifact(new RandomGeneratedInputStream(5 * 1024), sm.getId(), "file1", false); - assertThat(artifactManagement.findArtifact(result.getId())).isEqualTo(result); + assertThat(artifactManagement.findLocalArtifact(result.getId())).isEqualTo(result); } /** diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java index d5cc7f029..1229da2cd 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java @@ -937,7 +937,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { for (final Target myt : targets) { boolean found = false; for (final TargetAssignDistributionSetEvent event : events) { - if (event.getControllerId().equals(myt.getControllerId())) { + if (event.getTarget().getControllerId().equals(myt.getControllerId())) { found = true; final List activeActionsByTarget = deploymentManagement.findActiveActionsByTarget(myt); assertThat(activeActionsByTarget).as("size of active actions for target is wrong").isNotEmpty(); diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/TestConfiguration.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/TestConfiguration.java index 499925540..d665565a6 100644 --- a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/TestConfiguration.java +++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/TestConfiguration.java @@ -11,6 +11,8 @@ package org.eclipse.hawkbit; import java.util.concurrent.Executor; import java.util.concurrent.Executors; +import org.eclipse.hawkbit.api.ArtifactUrlHandlerProperties; +import org.eclipse.hawkbit.api.PropertyBasedArtifactUrlHandler; import org.eclipse.hawkbit.cache.CacheConstants; import org.eclipse.hawkbit.cache.TenancyCacheManager; import org.eclipse.hawkbit.cache.TenantAwareCacheManager; @@ -49,7 +51,8 @@ import com.mongodb.MongoClientOptions; */ @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true, mode = AdviceMode.PROXY, proxyTargetClass = false, securedEnabled = true) -@EnableConfigurationProperties({ HawkbitServerProperties.class, DdiSecurityProperties.class }) +@EnableConfigurationProperties({ HawkbitServerProperties.class, DdiSecurityProperties.class, + ArtifactUrlHandlerProperties.class }) @Profile("test") @EnableAutoConfiguration public class TestConfiguration implements AsyncConfigurer { @@ -63,6 +66,12 @@ public class TestConfiguration implements AsyncConfigurer { return new TestdataFactory(); } + @Bean + public PropertyBasedArtifactUrlHandler testPropertyBasedArtifactUrlHandler( + final ArtifactUrlHandlerProperties urlHandlerProperties) { + return new PropertyBasedArtifactUrlHandler(urlHandlerProperties); + } + @Bean public MongoClientOptions options() { return MongoClientOptions.builder().connectTimeout(500).maxWaitTime(500).connectionsPerHost(2) diff --git a/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/im/authentication/TenantUserPasswordAuthenticationToken.java b/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/im/authentication/TenantUserPasswordAuthenticationToken.java index 77beaa698..036d5a065 100644 --- a/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/im/authentication/TenantUserPasswordAuthenticationToken.java +++ b/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/im/authentication/TenantUserPasswordAuthenticationToken.java @@ -68,4 +68,35 @@ public class TenantUserPasswordAuthenticationToken extends UsernamePasswordAuthe public Object getTenant() { return tenant; } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((tenant == null) ? 0 : tenant.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final TenantUserPasswordAuthenticationToken other = (TenantUserPasswordAuthenticationToken) obj; + if (tenant == null) { + if (other.tenant != null) { + return false; + } + } else if (!tenant.equals(other.tenant)) { + return false; + } + return true; + } + } diff --git a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticateSecurityTokenFilter.java b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticateSecurityTokenFilter.java index 8ff1e9ebc..b953453ea 100644 --- a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticateSecurityTokenFilter.java +++ b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticateSecurityTokenFilter.java @@ -8,21 +8,16 @@ */ package org.eclipse.hawkbit.security; +import java.util.Optional; + import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken; -import org.eclipse.hawkbit.im.authentication.SpPermission; -import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails; import org.eclipse.hawkbit.repository.ControllerManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement; +import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.security.authentication.AnonymousAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.context.SecurityContextImpl; /** * An pre-authenticated processing filter which extracts (if enabled through @@ -68,11 +63,12 @@ public class ControllerPreAuthenticateSecurityTokenFilter extends AbstractContro @Override public HeaderAuthentication getPreAuthenticatedPrincipal(final TenantSecurityToken secruityToken) { + final String controllerId = resolveControllerId(secruityToken); final String authHeader = secruityToken.getHeader(TenantSecurityToken.AUTHORIZATION_HEADER); if ((authHeader != null) && authHeader.startsWith(TARGET_SECURITY_TOKEN_AUTH_SCHEME)) { LOGGER.debug("found authorization header with scheme {} using target security token for authentication", TARGET_SECURITY_TOKEN_AUTH_SCHEME); - return new HeaderAuthentication(secruityToken.getControllerId(), authHeader.substring(OFFSET_TARGET_TOKEN)); + return new HeaderAuthentication(controllerId, authHeader.substring(OFFSET_TARGET_TOKEN)); } LOGGER.debug( "security token filter is enabled but requst does not contain either the necessary path variables {} or the authorization header with scheme {}", @@ -81,51 +77,36 @@ public class ControllerPreAuthenticateSecurityTokenFilter extends AbstractContro } @Override - public HeaderAuthentication getPreAuthenticatedCredentials(final TenantSecurityToken secruityToken) { - final String securityToken = tenantAware.runAsTenant(secruityToken.getTenant(), - new GetSecurityTokenTenantRunner(secruityToken.getTenant(), secruityToken.getControllerId())); - return new HeaderAuthentication(secruityToken.getControllerId(), securityToken); + public HeaderAuthentication getPreAuthenticatedCredentials(final TenantSecurityToken securityToken) { + final Target target = systemSecurityContext.runAsSystemAsTenant(() -> { + if (securityToken.getTargetId() != null) { + return controllerManagement.findByTargetId(securityToken.getTargetId()); + } + return controllerManagement.findByControllerId(securityToken.getControllerId()); + }, securityToken.getTenant()); + + if (target == null) { + return null; + } + final String targetSecurityToken = systemSecurityContext.runAsSystemAsTenant(() -> target.getSecurityToken(), + securityToken.getTenant()); + return new HeaderAuthentication(target.getControllerId(), targetSecurityToken); + } + + private String resolveControllerId(final TenantSecurityToken securityToken) { + if (securityToken.getControllerId() != null) { + return securityToken.getControllerId(); + } + final Optional foundTarget = Optional.ofNullable(systemSecurityContext.runAsSystemAsTenant( + () -> controllerManagement.findByTargetId(securityToken.getTargetId()), securityToken.getTenant())); + if (!foundTarget.isPresent()) { + return null; + } + return foundTarget.get().getControllerId(); } @Override protected TenantConfigurationKey getTenantConfigurationKey() { return TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED; } - - private final class GetSecurityTokenTenantRunner implements TenantAware.TenantRunner { - - private final String controllerId; - private final String tenant; - - private GetSecurityTokenTenantRunner(final String tenant, final String controllerId) { - this.tenant = tenant; - this.controllerId = controllerId; - } - - @Override - public String run() { - LOGGER.trace("retrieving security token for controllerId {}", controllerId); - final SecurityContext oldContext = SecurityContextHolder.getContext(); - try { - SecurityContextHolder.setContext(getSecurityTokenReadContext()); - return controllerManagement.getSecurityTokenByControllerId(controllerId); - } finally { - SecurityContextHolder.setContext(oldContext); - } - } - - private SecurityContext getSecurityTokenReadContext() { - final SecurityContextImpl securityContextImpl = new SecurityContextImpl(); - securityContextImpl.setAuthentication(getSecurityTokenReadAuthentication()); - return securityContextImpl; - } - - private Authentication getSecurityTokenReadAuthentication() { - final AnonymousAuthenticationToken anonymousAuthenticationToken = new AnonymousAuthenticationToken( - "anonymous-read-security-token", "anonymous", com.google.common.collect.Lists - .newArrayList(new SimpleGrantedAuthority(SpPermission.READ_TARGET_SEC_TOKEN))); - anonymousAuthenticationToken.setDetails(new TenantAwareAuthenticationDetails(tenant, true)); - return anonymousAuthenticationToken; - } - } } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadLayout.java index 92f6a1f36..744590030 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadLayout.java @@ -594,7 +594,9 @@ public class UploadLayout extends VerticalLayout { // delete file system zombies artifactUploadState.getFileSelected().forEach(customFile -> { final File file = new File(customFile.getFilePath()); - file.delete(); + if (!file.delete()) { + LOG.warn("Failed to delete file {} in upload dialog", customFile.getFilePath()); + } }); artifactUploadState.getFileSelected().clear(); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DsMetadataPopupLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DsMetadataPopupLayout.java index 8b035de45..8c363837c 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DsMetadataPopupLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DsMetadataPopupLayout.java @@ -79,7 +79,6 @@ public class DsMetadataPopupLayout extends AbstractMetadataPopupLayout Date: Wed, 28 Sep 2016 14:39:39 +0200 Subject: [PATCH 31/43] hawkBit pom release preparations Signed-off-by: Michael Hirsch --- pom.xml | 45 +++++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/pom.xml b/pom.xml index 004c98e4e..a35752663 100644 --- a/pom.xml +++ b/pom.xml @@ -180,22 +180,6 @@ true - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.5 - true - - ossrh - https://oss.sonatype.org/ - false - - com.mycila license-maven-plugin @@ -413,6 +397,35 @@ + + nexus_staging + + true + + useNexusStaging + + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.5 + true + + ossrh + https://oss.sonatype.org/ + false + + + + + create_gpg_signature From a23b6bcc409ec9c16fb5677df6d1f296c56336d1 Mon Sep 17 00:00:00 2001 From: Michael Hirsch Date: Wed, 28 Sep 2016 15:57:52 +0200 Subject: [PATCH 32/43] hawkBit pom release preparations Signed-off-by: Michael Hirsch --- pom.xml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index a35752663..6e0f26287 100644 --- a/pom.xml +++ b/pom.xml @@ -400,9 +400,8 @@ nexus_staging - true - useNexusStaging + !skipNexusStaging @@ -415,7 +414,7 @@ as part of building the module. --> org.sonatype.plugins nexus-staging-maven-plugin - 1.6.5 + 1.6.7 true ossrh From e08962cf8f84fc614084df44a61d5cd57b90af94 Mon Sep 17 00:00:00 2001 From: kaizimmerm Date: Fri, 30 Sep 2016 08:53:58 +0200 Subject: [PATCH 33/43] Fixed broken DMF API collection handling Signed-off-by: kaizimmerm --- .../eclipse/hawkbit/simulator/amqp/SpSenderService.java | 2 +- .../hawkbit/dmf/json/model/ActionUpdateStatus.java | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/SpSenderService.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/SpSenderService.java index 1ced8c2fd..f384347c6 100644 --- a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/SpSenderService.java +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/SpSenderService.java @@ -206,7 +206,7 @@ public class SpSenderService extends SenderService { headers.put(MessageHeaderKey.TENANT, cacheValue.getTenant()); headers.put(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name()); headers.put(MessageHeaderKey.CONTENT_TYPE, MessageProperties.CONTENT_TYPE_JSON); - actionUpdateStatus.getMessage().addAll(updateResultMessages); + actionUpdateStatus.addMessage(updateResultMessages); actionUpdateStatus.setActionId(cacheValue.getActionId()); return convertMessage(actionUpdateStatus, messageProperties); } diff --git a/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/ActionUpdateStatus.java b/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/ActionUpdateStatus.java index 2262cab96..652039e35 100644 --- a/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/ActionUpdateStatus.java +++ b/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/ActionUpdateStatus.java @@ -9,6 +9,7 @@ package org.eclipse.hawkbit.dmf.json.model; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; @@ -72,4 +73,12 @@ public class ActionUpdateStatus { return this.message.add(message); } + public boolean addMessage(final Collection messages) { + if (this.message == null) { + this.message = new ArrayList<>(); + } + + return this.message.addAll(messages); + } + } From de6b5cd93d3b378e4c9cf8e1c3adfc74d555d2ac Mon Sep 17 00:00:00 2001 From: kaizimmerm Date: Fri, 30 Sep 2016 09:12:39 +0200 Subject: [PATCH 34/43] Optimized array allocation Signed-off-by: kaizimmerm --- .../eclipse/hawkbit/dmf/json/model/ActionUpdateStatus.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/ActionUpdateStatus.java b/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/ActionUpdateStatus.java index 652039e35..f17252bd8 100644 --- a/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/ActionUpdateStatus.java +++ b/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/ActionUpdateStatus.java @@ -74,8 +74,13 @@ public class ActionUpdateStatus { } public boolean addMessage(final Collection messages) { + if (messages == null || messages.isEmpty()) { + return false; + } + if (this.message == null) { - this.message = new ArrayList<>(); + this.message = new ArrayList<>(messages); + return true; } return this.message.addAll(messages); From fa3cee62bf44f06aa68f530ca4b46840adc54f15 Mon Sep 17 00:00:00 2001 From: Jonathan Philip Knoblauch Date: Fri, 30 Sep 2016 13:04:58 +0200 Subject: [PATCH 35/43] Refactoring after review Signed-off-by: Jonathan Philip Knoblauch --- .../jpa/rsql/RsqlParserValidationOracle.java | 129 +++++++++--------- 1 file changed, 68 insertions(+), 61 deletions(-) diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlParserValidationOracle.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlParserValidationOracle.java index 641b9a291..a3076b6f0 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlParserValidationOracle.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlParserValidationOracle.java @@ -112,39 +112,38 @@ public class RsqlParserValidationOracle implements RsqlValidationOracle { private static List getNextTokens(final Exception e) { final ParseException parseException = findParseException(e); - if (parseException != null) { - final List listTokens = new ArrayList<>(); - final ParseExceptionWrapper parseExceptionWrapper = new ParseExceptionWrapper(parseException); - final int[][] expectedTokenSequence = parseExceptionWrapper.getExpectedTokenSequence(); - final TokenWrapper currentToken = parseExceptionWrapper.getCurrentToken(); - final TokenWrapper nextToken = currentToken.getNext(); - final int currentTokenKind = currentToken.getKind(); - final String currentTokenImageName = currentToken.getImage(); - final int nextTokenBeginColumn = nextToken.getBeginColumn(); - final int currentTokenEndColumn = currentToken.getEndColumn(); - - // token == 5 is the field token, reverse engineering. - if (currentTokenKind == 5) { - final Optional> handleFieldTokenSuggestion = handleFieldTokenSuggestion( - currentTokenImageName, nextTokenBeginColumn, currentTokenEndColumn); - if (handleFieldTokenSuggestion.isPresent()) { - return handleFieldTokenSuggestion.get(); - } - } - - for (final int[] is : expectedTokenSequence) { - for (final int i : is) { - final Collection tokenImage = TokenDescription.getTokenImage(i); - if (tokenImage != null && !tokenImage.isEmpty()) { - tokenImage.forEach(image -> listTokens.add(new SuggestToken(currentTokenEndColumn + 1, - nextTokenBeginColumn + image.length(), null, image))); - } - } - } - return listTokens; + if (parseException == null) { + return Collections.emptyList(); } - return Collections.emptyList(); + final List listTokens = new ArrayList<>(); + final ParseExceptionWrapper parseExceptionWrapper = new ParseExceptionWrapper(parseException); + final int[][] expectedTokenSequence = parseExceptionWrapper.getExpectedTokenSequence(); + final TokenWrapper currentToken = parseExceptionWrapper.getCurrentToken(); + final TokenWrapper nextToken = currentToken.getNext(); + final int currentTokenKind = currentToken.getKind(); + final String currentTokenImageName = currentToken.getImage(); + final int nextTokenBeginColumn = nextToken.getBeginColumn(); + final int currentTokenEndColumn = currentToken.getEndColumn(); + // token == 5 is the field token, reverse engineering. + if (currentTokenKind == 5) { + final Optional> handleFieldTokenSuggestion = handleFieldTokenSuggestion( + currentTokenImageName, nextTokenBeginColumn, currentTokenEndColumn); + if (handleFieldTokenSuggestion.isPresent()) { + return handleFieldTokenSuggestion.get(); + } + } + + for (final int[] is : expectedTokenSequence) { + for (final int i : is) { + final Collection tokenImage = TokenDescription.getTokenImage(i); + if (tokenImage != null && !tokenImage.isEmpty()) { + tokenImage.forEach(image -> listTokens.add(new SuggestToken(currentTokenEndColumn + 1, + nextTokenBeginColumn + image.length(), null, image))); + } + } + } + return listTokens; } private static Optional> handleFieldTokenSuggestion(final String currentTokenImageName, @@ -193,9 +192,9 @@ public class RsqlParserValidationOracle implements RsqlValidationOracle { } private static ParseException findParseException(final Throwable e) { - if (e != null && e instanceof ParseException) { + if (e instanceof ParseException) { return (ParseException) e; - } else if (e != null && e.getCause() != null) { + } else if (e.getCause() != null) { return findParseException(e.getCause()); } return null; @@ -203,24 +202,31 @@ public class RsqlParserValidationOracle implements RsqlValidationOracle { private static String getCustomMessage(final String message, final List expectedTokens) { String builder = message; - if (message.contains(":")) { - builder = message.substring(message.indexOf(':') + 1, message.length()); - if (builder.indexOf("Was expecting") != -1) { - builder = builder.substring(0, builder.lastIndexOf("Was expecting")); - } - if (null != expectedTokens && !expectedTokens.isEmpty()) { - final StringBuilder tokens = new StringBuilder(); - expectedTokens.stream().forEach(value -> tokens.append(value.getSuggestion() + ",")); - builder = builder.concat("Was expecting :" + tokens.toString().substring(0, tokens.length() - 1)); - } - builder = builder.replace('\r', ' '); - builder = builder.replace('\n', ' '); - builder = builder.replaceAll(">", " "); - builder = builder.replaceAll("<", " "); + + if (!message.contains(":")) { + return builder; } + + builder = message.substring(message.indexOf(':') + 1, message.length()); + if (builder.indexOf("Was expecting") != -1) { + builder = builder.substring(0, builder.lastIndexOf("Was expecting")); + } + + if (expectedTokens != null && !expectedTokens.isEmpty()) { + final StringBuilder tokens = new StringBuilder(); + expectedTokens.stream().forEach(value -> tokens.append(value.getSuggestion() + ",")); + builder = builder.concat("Was expecting :" + tokens.toString().substring(0, tokens.length() - 1)); + } + builder = builder.replace('\r', ' '); + builder = builder.replace('\n', ' '); + builder = builder.replaceAll(">", " "); + builder = builder.replaceAll("<", " "); + return builder; } + // Token map with logical and comparator operator that are used for context + // sensitive help on search query. private static final class TokenDescription { private static final Multimap TOKEN_MAP = ArrayListMultimap.create(); @@ -264,18 +270,17 @@ public class RsqlParserValidationOracle implements RsqlValidationOracle { } private static boolean hasSubEntries(final String tokenImageName) { - final String tmpTokenName; + String tmpTokenName = tokenImageName; if (tokenImageName.contains(".")) { final String[] split = tokenImageName.split("\\."); - if (split.length > 0) { - tmpTokenName = split[0]; - } else { + if (split.length <= 0) { return false; } - } else { - tmpTokenName = tokenImageName; + tmpTokenName = split[0]; } - return Arrays.stream(TargetFields.values()).filter(field -> field.toString().equalsIgnoreCase(tmpTokenName)) + final String finalTmpTokenName = tmpTokenName; + return Arrays.stream(TargetFields.values()) + .filter(field -> field.toString().equalsIgnoreCase(finalTmpTokenName)) .map(field -> field.getSubEntityAttributes()).flatMap(subentities -> subentities.stream()) .count() > 0; } @@ -296,16 +301,18 @@ public class RsqlParserValidationOracle implements RsqlValidationOracle { } private static boolean containsValue(final String imageName) { - if (imageName.contains(".")) { - final String[] split = imageName.split("\\."); - if (split.length > 1 && FIELD_NAMES.contains(split[0].toLowerCase())) { - return SUB_NAMES.get(split[0].toLowerCase()).stream() - .filter(subname -> new String(split[0] + "." + subname).equalsIgnoreCase(imageName)) - .count() > 0; - } + if (!imageName.contains(".")) { + return FIELD_NAMES.stream().filter(value -> value.equalsIgnoreCase(imageName)).count() > 0; + } + final String[] split = imageName.split("\\."); + if (split.length > 1 && FIELD_NAMES.contains(split[0].toLowerCase())) { + return SUB_NAMES.get(split[0].toLowerCase()).stream() + .filter(subname -> new String(split[0] + "." + subname).equalsIgnoreCase(imageName)) + .count() > 0; } return FIELD_NAMES.stream().filter(value -> value.equalsIgnoreCase(imageName)).count() > 0; } + } } From 8c7af7d28dca14b0814b4b959c1d17ae1b1ae45b Mon Sep 17 00:00:00 2001 From: Jonathan Philip Knoblauch Date: Tue, 4 Oct 2016 10:16:08 +0200 Subject: [PATCH 36/43] More refactoring (added early returns) Signed-off-by: Jonathan Philip Knoblauch --- .../jpa/rsql/RsqlParserValidationOracle.java | 7 +++++-- .../CreateOrUpdateFilterHeader.java | 10 +++++++--- .../client/AutoCompleteTextFieldConnector.java | 18 ++++++++++-------- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlParserValidationOracle.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlParserValidationOracle.java index a3076b6f0..adec49389 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlParserValidationOracle.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlParserValidationOracle.java @@ -92,9 +92,12 @@ public class RsqlParserValidationOracle implements RsqlValidationOracle { } private static Collection getLogicalOperatorSuggestion(final String rsqlQuery) { - final int currentQueryLength = rsqlQuery.length(); - // only return and/or suggestion when there is a space at the end + if (!rsqlQuery.endsWith(" ")) { + return Collections.emptyList(); + } if (rsqlQuery.endsWith(" ")) { + final int currentQueryLength = rsqlQuery.length(); + // only return and/or suggestion when there is a space at the end final Collection tokenImages = TokenDescription.getTokenImage(TokenDescription.LOGICAL_OP); final List logicalOps = new ArrayList<>(tokenImages.size()); for (final String tokenImage : tokenImages) { diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterHeader.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterHeader.java index b9e98c9f0..f508aa436 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterHeader.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterHeader.java @@ -379,10 +379,14 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button } private void onSearchIconClick() { - if (!queryTextField.isValidationError()) { - queryTextField.showValidationInProgress(); - queryTextField.getExecutor().execute(queryTextField.new StatusCircledAsync(UI.getCurrent())); + + if (queryTextField.isValidationError()) { + return; } + + queryTextField.showValidationInProgress(); + queryTextField.getExecutor().execute(queryTextField.new StatusCircledAsync(UI.getCurrent())); + } @Override diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/AutoCompleteTextFieldConnector.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/AutoCompleteTextFieldConnector.java index 8b5812bf1..80f7714b7 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/AutoCompleteTextFieldConnector.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/client/AutoCompleteTextFieldConnector.java @@ -54,14 +54,16 @@ public class AutoCompleteTextFieldConnector extends AbstractExtensionConnector { @Override public void showSuggestions(final SuggestionContextDto suggestContext) { select.clearItems(); - if (suggestContext != null) { - final List suggestions = suggestContext.getSuggestions(); - if (suggestions != null && !suggestions.isEmpty()) { - select.addItems(suggestions, textFieldWidget, panel, rpc); - panel.showRelativeTo(textFieldWidget); - select.moveSelectionDown(); - return; - } + if (suggestContext == null) { + panel.hide(); + return; + } + final List suggestions = suggestContext.getSuggestions(); + if (suggestions != null && !suggestions.isEmpty()) { + select.addItems(suggestions, textFieldWidget, panel, rpc); + panel.showRelativeTo(textFieldWidget); + select.moveSelectionDown(); + return; } panel.hide(); } From a36249f6dd80cc39c24ac1dbdd9c7b205d337b3f Mon Sep 17 00:00:00 2001 From: Kai Zimmermann Date: Tue, 4 Oct 2016 12:56:58 +0200 Subject: [PATCH 37/43] Fix DMF api break (#306) Fixed upper to lowever case change that was introduced with the new property based JSon generator. Could be considered an API break. Signed-off-by: kaizimmerm --- .../eclipse/hawkbit/simulator/DeviceSimulatorUpdater.java | 8 ++++---- .../hawkbit/api/PropertyBasedArtifactUrlHandler.java | 2 +- .../hawkbit/api/PropertyBasedArtifactUrlHandlerTest.java | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DeviceSimulatorUpdater.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DeviceSimulatorUpdater.java index e73244247..a0b607b83 100644 --- a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DeviceSimulatorUpdater.java +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DeviceSimulatorUpdater.java @@ -205,11 +205,11 @@ public class DeviceSimulatorUpdater { private static void handleArtifacts(final String targetToken, final List status, final Artifact artifact) { - if (artifact.getUrls().containsKey("https")) { - status.add(downloadUrl(artifact.getUrls().get("https"), targetToken, artifact.getHashes().getSha1(), + if (artifact.getUrls().containsKey("HTTPS")) { + status.add(downloadUrl(artifact.getUrls().get("HTTPS"), targetToken, artifact.getHashes().getSha1(), artifact.getSize())); - } else if (artifact.getUrls().containsKey("http")) { - status.add(downloadUrl(artifact.getUrls().get("http"), targetToken, artifact.getHashes().getSha1(), + } else if (artifact.getUrls().containsKey("HTTP")) { + status.add(downloadUrl(artifact.getUrls().get("HTTP"), targetToken, artifact.getHashes().getSha1(), artifact.getSize())); } } diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/PropertyBasedArtifactUrlHandler.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/PropertyBasedArtifactUrlHandler.java index 229438477..cb495d7fb 100644 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/PropertyBasedArtifactUrlHandler.java +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/PropertyBasedArtifactUrlHandler.java @@ -72,7 +72,7 @@ public class PropertyBasedArtifactUrlHandler implements ArtifactUrlHandler { return urlHandlerProperties.getProtocols().entrySet().stream() .filter(entry -> entry.getValue().getSupports().contains(api)) .filter(entry -> entry.getValue().isEnabled()) - .map(entry -> new ArtifactUrl(entry.getValue().getProtocol(), entry.getValue().getRel(), + .map(entry -> new ArtifactUrl(entry.getValue().getProtocol().toUpperCase(), entry.getValue().getRel(), generateUrl(entry.getValue(), placeholder))) .collect(Collectors.toList()); diff --git a/hawkbit-core/src/test/java/org/eclipse/hawkbit/api/PropertyBasedArtifactUrlHandlerTest.java b/hawkbit-core/src/test/java/org/eclipse/hawkbit/api/PropertyBasedArtifactUrlHandlerTest.java index 3d7fc18af..0a865a9ad 100644 --- a/hawkbit-core/src/test/java/org/eclipse/hawkbit/api/PropertyBasedArtifactUrlHandlerTest.java +++ b/hawkbit-core/src/test/java/org/eclipse/hawkbit/api/PropertyBasedArtifactUrlHandlerTest.java @@ -70,8 +70,8 @@ public class PropertyBasedArtifactUrlHandlerTest { properties.getProtocols().put("download-http", new UrlProtocol()); final List ddiUrls = urlHandlerUnderTest.getUrls(placeholder, ApiType.DDI); - assertEquals( - Lists.newArrayList(new ArtifactUrl("http", "download-http", HTTP_LOCALHOST + TENANT + "/controller/v1/" + assertEquals(Lists.newArrayList( + new ArtifactUrl("http".toUpperCase(), "download-http", HTTP_LOCALHOST + TENANT + "/controller/v1/" + CONTROLLER_ID + "/softwaremodules/" + SOFTWAREMODULEID + "/artifacts/" + FILENAME)), ddiUrls); @@ -96,7 +96,7 @@ public class PropertyBasedArtifactUrlHandlerTest { assertThat(urls).isEmpty(); urls = urlHandlerUnderTest.getUrls(placeholder, ApiType.DMF); - assertEquals(Lists.newArrayList(new ArtifactUrl(TEST_PROTO, TEST_REL, + assertEquals(Lists.newArrayList(new ArtifactUrl(TEST_PROTO.toUpperCase(), TEST_REL, "coap://127.0.0.1:5683/fw/" + TENANT + "/" + CONTROLLER_ID + "/sha1/" + SHA1HASH)), urls); } @@ -117,7 +117,7 @@ public class PropertyBasedArtifactUrlHandlerTest { assertThat(urls).isEmpty(); urls = urlHandlerUnderTest.getUrls(placeholder, ApiType.DMF); - assertEquals(Lists.newArrayList(new ArtifactUrl(TEST_PROTO, TEST_REL, + assertEquals(Lists.newArrayList(new ArtifactUrl(TEST_PROTO.toUpperCase(), TEST_REL, TEST_PROTO + "://127.0.0.1:5683/fws/" + TENANT + "/" + TARGETID_BASE62 + "/" + ARTIFACTID_BASE62)), urls); } From f139c8dafd03e0b78033f3a7c53782e5cfc8e501 Mon Sep 17 00:00:00 2001 From: Michael Hirsch Date: Thu, 6 Oct 2016 09:02:45 +0200 Subject: [PATCH 38/43] skip example maven deploy, due the size of the example app Signed-off-by: Michael Hirsch --- examples/pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/examples/pom.xml b/examples/pom.xml index 85dd70651..66bce5fc1 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -21,6 +21,11 @@ hawkbit-examples-parent hawkBit-example :: Parent pom + + + + true + hawkbit-device-simulator From c018fdd039c0d81efdaf28d20765ed48fa557f3b Mon Sep 17 00:00:00 2001 From: Michael Hirsch Date: Thu, 6 Oct 2016 09:39:14 +0200 Subject: [PATCH 39/43] remove examples from maven-modules. Signed-off-by: Michael Hirsch --- README.md | 5 +++++ pom.xml | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 778391f32..0157e089e 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,11 @@ $ git clone https://github.com/eclipse/hawkbit.git $ cd hawkbit $ mvn clean install ``` +Build hawkBit examples. The examples are not included in the maven-reactor build. +``` +$ cd hawkbit/examples +$ mvn clean install +``` #### Start hawkBit example app [Example Application](examples/hawkbit-example-app) ``` diff --git a/pom.xml b/pom.xml index 2084b92f6..7106332f9 100644 --- a/pom.xml +++ b/pom.xml @@ -42,7 +42,6 @@ hawkbit-autoconfigure hawkbit-cache-redis hawkbit-test-report - examples From 3baacf791505d3d03dfb6312a4324830aa75f32a Mon Sep 17 00:00:00 2001 From: Michael Hirsch Date: Thu, 6 Oct 2016 10:20:10 +0200 Subject: [PATCH 40/43] enable test-report generation via profile Signed-off-by: Michael Hirsch --- hawkbit-test-report/pom.xml | 115 ++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 57 deletions(-) diff --git a/hawkbit-test-report/pom.xml b/hawkbit-test-report/pom.xml index 3e9182f0a..77e416d2c 100644 --- a/hawkbit-test-report/pom.xml +++ b/hawkbit-test-report/pom.xml @@ -8,9 +8,7 @@ http://www.eclipse.org/legal/epl-v10.html --> - - + 4.0.0 org.eclipse.hawkbit @@ -22,60 +20,63 @@ pom - - - - maven-resources-plugin - 2.7 - - - copy-resources - verify - - copy-resources - + + + generateTestReport + + + + maven-resources-plugin + + + copy-resources + verify + + copy-resources + + + ${basedir}/target/allure-results + + + ${basedir}/.. + false + + **/target/allure-results/*.xml + + + + ${basedir} + false + + placeholder.txt + + + + + + + + + org.apache.maven.plugins + maven-assembly-plugin - ${basedir}/target/allure-results - - - ${basedir}/.. - false - - **/target/allure-results/*.xml - - - - ${basedir} - false - - placeholder.txt - - - + + src/main/assembly/test-report.xml + + false - - - - - org.apache.maven.plugins - maven-assembly-plugin - 2.5.3 - - - src/main/assembly/test-report.xml - - false - - - - create-report-zip-assembly - verify - - single - - - - - - + + + create-report-zip-assembly + verify + + single + + + + + + + + \ No newline at end of file From c427503c19b6565808fc0887f78f9dafa4d14dad Mon Sep 17 00:00:00 2001 From: Michael Hirsch Date: Thu, 6 Oct 2016 11:25:04 +0200 Subject: [PATCH 41/43] change scm information for releasing hawkbit Signed-off-by: Michael Hirsch --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7106332f9..4156620ff 100644 --- a/pom.xml +++ b/pom.xml @@ -148,7 +148,7 @@ - scm:git:https://github.com/eclipse/hawkbit.git + scm:git:git@github.com:eclipse/hawkbit.git scm:git:https://github.com/eclipse/hawkbit.git https://github.com/eclipse/hawkbit.git From e469b8fad224bfa299ff51747168043559f734d4 Mon Sep 17 00:00:00 2001 From: kaizimmerm Date: Thu, 6 Oct 2016 16:14:23 +0200 Subject: [PATCH 42/43] reenabled examples on Bosch fork to allo IoT SP release. Signed-off-by: kaizimmerm --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 7106332f9..2084b92f6 100644 --- a/pom.xml +++ b/pom.xml @@ -42,6 +42,7 @@ hawkbit-autoconfigure hawkbit-cache-redis hawkbit-test-report + examples From 0f9da64c7e488513b031d6081469fe5e57daf3d6 Mon Sep 17 00:00:00 2001 From: Michael Hirsch Date: Fri, 7 Oct 2016 08:26:46 +0200 Subject: [PATCH 43/43] introduce nonExampleApp profile for building examples without boot apps Signed-off-by: Michael Hirsch --- README.md | 5 ----- examples/pom.xml | 41 ++++++++++++++++++++++++++--------------- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 0157e089e..778391f32 100644 --- a/README.md +++ b/README.md @@ -33,11 +33,6 @@ $ git clone https://github.com/eclipse/hawkbit.git $ cd hawkbit $ mvn clean install ``` -Build hawkBit examples. The examples are not included in the maven-reactor build. -``` -$ cd hawkbit/examples -$ mvn clean install -``` #### Start hawkBit example app [Example Application](examples/hawkbit-example-app) ``` diff --git a/examples/pom.xml b/examples/pom.xml index 66bce5fc1..5f8718e0a 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -8,8 +8,7 @@ http://www.eclipse.org/legal/epl-v10.html --> - + 4.0.0 @@ -21,20 +20,32 @@ hawkbit-examples-parent hawkBit-example :: Parent pom - - - - true - - - hawkbit-device-simulator - hawkbit-example-app - hawkbit-example-core-feign-client - hawkbit-example-ddi-feign-client - hawkbit-example-mgmt-feign-client - hawkbit-example-mgmt-simulator - + + + noExampleApp + + hawkbit-example-core-feign-client + hawkbit-example-ddi-feign-client + hawkbit-example-mgmt-feign-client + hawkbit-example-mgmt-simulator + + + + default + + true + + + hawkbit-device-simulator + hawkbit-example-app + hawkbit-example-core-feign-client + hawkbit-example-ddi-feign-client + hawkbit-example-mgmt-feign-client + hawkbit-example-mgmt-simulator + + +