diff --git a/hawkbit-repository/hawkbit-repository-api/pom.xml b/hawkbit-repository/hawkbit-repository-api/pom.xml
index 541770d1a..d1a0df30a 100644
--- a/hawkbit-repository/hawkbit-repository-api/pom.xml
+++ b/hawkbit-repository/hawkbit-repository-api/pom.xml
@@ -67,6 +67,10 @@
com.cronutils
cron-utils
+
+ cz.jirutka.rsql
+ rsql-parser
+
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/rsql/RsqlVisitorFactory.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/rsql/RsqlVisitorFactory.java
new file mode 100644
index 000000000..ab93f87c5
--- /dev/null
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/rsql/RsqlVisitorFactory.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2021 Bosch.IO 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 org.eclipse.hawkbit.repository.FieldNameProvider;
+
+import cz.jirutka.rsql.parser.ast.Node;
+import cz.jirutka.rsql.parser.ast.RSQLVisitor;
+
+/**
+ * Factory to obtain {@link RSQLVisitor} instances that can be used to process
+ * the {@link Node}s representing an RSQL query.
+ */
+@FunctionalInterface
+public interface RsqlVisitorFactory {
+
+ /**
+ * Provides a {@link RSQLVisitor} instance for validating RSQL queries based
+ * on the given {@link FieldNameProvider}.
+ *
+ * @param
+ * The type of the {@link FieldNameProvider}.
+ * @param fieldNameProvider
+ * providing accessing to the relevant field names.
+ *
+ * @return An {@link RSQLVisitor} to validate the {@link Node}s of an RSQL
+ * query.
+ */
+ & FieldNameProvider> RSQLVisitor validationRsqlVisitor(Class fieldNameProvider);
+
+}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/rsql/RsqlVisitorFactoryHolder.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/rsql/RsqlVisitorFactoryHolder.java
new file mode 100644
index 000000000..98f0d8fe5
--- /dev/null
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/rsql/RsqlVisitorFactoryHolder.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2021 Bosch.IO 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 org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * Helper class providing static access to the managed
+ * {@link RsqlVisitorFactory} bean.
+ */
+public final class RsqlVisitorFactoryHolder {
+
+ private static final RsqlVisitorFactoryHolder SINGLETON = new RsqlVisitorFactoryHolder();
+
+ @Autowired
+ private RsqlVisitorFactory rsqlVisitorFactory;
+
+ private RsqlVisitorFactoryHolder() {
+
+ }
+
+ /**
+ * @return The holder singleton instance.
+ */
+ public static RsqlVisitorFactoryHolder getInstance() {
+ return SINGLETON;
+ }
+
+ /**
+ * @return The managed RsqlVisitorFactory bean
+ */
+ public RsqlVisitorFactory getRsqlVisitorFactory() {
+ return rsqlVisitorFactory;
+ }
+
+}
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 82d5b4fc1..e564b40e0 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
@@ -655,7 +655,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
}
private Specification createSpecificationFor(final String controllerId, final String rsqlParam) {
- final Specification spec = RSQLUtility.parse(rsqlParam, ActionFields.class, virtualPropertyReplacer,
+ final Specification spec = RSQLUtility.buildRsqlSpecification(rsqlParam, ActionFields.class, virtualPropertyReplacer,
database);
return (root, query, cb) -> cb.and(spec.toPredicate(root, query, cb),
cb.equal(root.get(JpaAction_.target).get(JpaTarget_.controllerId), controllerId));
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 abc9465fd..9f652a455 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
@@ -555,7 +555,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
throwExceptionIfDistributionSetDoesNotExist(distributionSetId);
- final Specification spec = RSQLUtility.parse(rsqlParam,
+ final Specification spec = RSQLUtility.buildRsqlSpecification(rsqlParam,
DistributionSetMetadataFields.class, virtualPropertyReplacer, database);
return convertMdPage(
@@ -784,7 +784,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
public Page findByRsqlAndTag(final Pageable pageable, final String rsqlParam, final long tagId) {
throwEntityNotFoundExceptionIfDsTagDoesNotExist(tagId);
- final Specification spec = RSQLUtility.parse(rsqlParam, DistributionSetFields.class,
+ final Specification spec = RSQLUtility.buildRsqlSpecification(rsqlParam, DistributionSetFields.class,
virtualPropertyReplacer, database);
return convertDsPage(findByCriteriaAPI(pageable, Arrays.asList(spec, DistributionSetSpecification.hasTag(tagId),
@@ -799,7 +799,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override
public Page findByRsql(final Pageable pageable, final String rsqlParam) {
- final Specification spec = RSQLUtility.parse(rsqlParam, DistributionSetFields.class,
+ final Specification spec = RSQLUtility.buildRsqlSpecification(rsqlParam, DistributionSetFields.class,
virtualPropertyReplacer, database);
return convertDsPage(
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetTagManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetTagManagement.java
index 37344deee..fca185f48 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetTagManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetTagManagement.java
@@ -134,7 +134,7 @@ public class JpaDistributionSetTagManagement implements DistributionSetTagManage
@Override
public Page findByRsql(final Pageable pageable, final String rsqlParam) {
- final Specification spec = RSQLUtility.parse(rsqlParam, TagFields.class,
+ final Specification spec = RSQLUtility.buildRsqlSpecification(rsqlParam, TagFields.class,
virtualPropertyReplacer, database);
return convertDsPage(distributionSetTagRepository.findAll(spec, pageable), pageable);
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetTypeManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetTypeManagement.java
index 390ac3c12..b8cb28dfb 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetTypeManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetTypeManagement.java
@@ -227,7 +227,7 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
public Page findByRsql(final Pageable pageable, final String rsqlParam) {
return convertPage(
findByCriteriaAPI(pageable,
- Arrays.asList(RSQLUtility.parse(rsqlParam, DistributionSetTypeFields.class,
+ Arrays.asList(RSQLUtility.buildRsqlSpecification(rsqlParam, DistributionSetTypeFields.class,
virtualPropertyReplacer, database), DistributionSetTypeSpecification.isDeleted(false))),
pageable);
}
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 80a990f32..721ea1fb9 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
@@ -119,7 +119,7 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
final String rsqlParam) {
throwEntityNotFoundExceptionIfRolloutDoesNotExist(rolloutId);
- final Specification specification = RSQLUtility.parse(rsqlParam, RolloutGroupFields.class,
+ final Specification specification = RSQLUtility.buildRsqlSpecification(rsqlParam, RolloutGroupFields.class,
virtualPropertyReplacer, database);
return convertPage(rolloutGroupRepository.findAll((root, query, criteriaBuilder) -> criteriaBuilder.and(
@@ -211,7 +211,7 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
throwExceptionIfRolloutGroupDoesNotExist(rolloutGroupId);
- final Specification rsqlSpecification = RSQLUtility.parse(rsqlParam, TargetFields.class,
+ final Specification rsqlSpecification = RSQLUtility.buildRsqlSpecification(rsqlParam, TargetFields.class,
virtualPropertyReplacer, database);
return convertTPage(targetRepository.findAll((root, query, criteriaBuilder) -> {
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 73e21945c..1d45755d0 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
@@ -151,7 +151,7 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
@Override
public Page findByRsql(final Pageable pageable, final String rsqlParam, final boolean deleted) {
final List> specList = Lists.newArrayListWithExpectedSize(2);
- specList.add(RSQLUtility.parse(rsqlParam, RolloutFields.class, virtualPropertyReplacer, database));
+ specList.add(RSQLUtility.buildRsqlSpecification(rsqlParam, RolloutFields.class, virtualPropertyReplacer, database));
specList.add(RolloutSpecification.isDeletedWithDistributionSet(deleted));
return JpaRolloutHelper.convertPage(findByCriteriaAPI(pageable, specList), pageable);
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareModuleManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareModuleManagement.java
index ea85e11f6..d35da4ea4 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareModuleManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareModuleManagement.java
@@ -300,7 +300,7 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
@Override
public Page findByRsql(final Pageable pageable, final String rsqlParam) {
- final Specification spec = RSQLUtility.parse(rsqlParam, SoftwareModuleFields.class,
+ final Specification spec = RSQLUtility.buildRsqlSpecification(rsqlParam, SoftwareModuleFields.class,
virtualPropertyReplacer, database);
return convertSmPage(softwareModuleRepository.findAll(spec, pageable), pageable);
@@ -608,7 +608,7 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
throwExceptionIfSoftwareModuleDoesNotExist(softwareModuleId);
- final Specification spec = RSQLUtility.parse(rsqlParam,
+ final Specification spec = RSQLUtility.buildRsqlSpecification(rsqlParam,
SoftwareModuleMetadataFields.class, virtualPropertyReplacer, database);
return convertSmMdPage(
softwareModuleMetadataRepository
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareModuleTypeManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareModuleTypeManagement.java
index a9eb11c7a..3e9fbc514 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareModuleTypeManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareModuleTypeManagement.java
@@ -90,7 +90,7 @@ public class JpaSoftwareModuleTypeManagement implements SoftwareModuleTypeManage
@Override
public Page findByRsql(final Pageable pageable, final String rsqlParam) {
- final Specification spec = RSQLUtility.parse(rsqlParam, SoftwareModuleTypeFields.class,
+ final Specification spec = RSQLUtility.buildRsqlSpecification(rsqlParam, SoftwareModuleTypeFields.class,
virtualPropertyReplacer, database);
return convertPage(softwareModuleTypeRepository.findAll(spec, pageable), pageable);
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetFilterQueryManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetFilterQueryManagement.java
index 91a9a247c..1c169109d 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetFilterQueryManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetFilterQueryManagement.java
@@ -166,7 +166,7 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
List> specList = Collections.emptyList();
if (!StringUtils.isEmpty(rsqlFilter)) {
specList = Collections.singletonList(
- RSQLUtility.parse(rsqlFilter, TargetFilterQueryFields.class, virtualPropertyReplacer, database));
+ RSQLUtility.buildRsqlSpecification(rsqlFilter, TargetFilterQueryFields.class, virtualPropertyReplacer, database));
}
return convertPage(findTargetFilterQueryByCriteriaAPI(pageable, specList), pageable);
}
@@ -191,7 +191,7 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
if (!StringUtils.isEmpty(rsqlFilter)) {
specList.add(
- RSQLUtility.parse(rsqlFilter, TargetFilterQueryFields.class, virtualPropertyReplacer, database));
+ RSQLUtility.buildRsqlSpecification(rsqlFilter, TargetFilterQueryFields.class, virtualPropertyReplacer, database));
}
return convertPage(findTargetFilterQueryByCriteriaAPI(pageable, specList), pageable);
}
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 e2549c32f..105911edc 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
@@ -282,7 +282,7 @@ public class JpaTargetManagement implements TargetManagement {
final Long targetId = getByControllerIdAndThrowIfNotFound(controllerId).getId();
- final Specification spec = RSQLUtility.parse(rsqlParam, TargetMetadataFields.class,
+ final Specification spec = RSQLUtility.buildRsqlSpecification(rsqlParam, TargetMetadataFields.class,
virtualPropertyReplacer, database);
return convertMdPage(targetMetadataRepository.findAll((Specification) (root, query, cb) -> cb
@@ -309,14 +309,14 @@ public class JpaTargetManagement implements TargetManagement {
.orElseThrow(() -> new EntityNotFoundException(TargetFilterQuery.class, targetFilterQueryId));
return findTargetsBySpec(
- RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class, virtualPropertyReplacer, database),
+ RSQLUtility.buildRsqlSpecification(targetFilterQuery.getQuery(), TargetFields.class, virtualPropertyReplacer, database),
pageable);
}
@Override
public Page findByRsql(final Pageable pageable, final String targetFilterQuery) {
return findTargetsBySpec(
- RSQLUtility.parse(targetFilterQuery, TargetFields.class, virtualPropertyReplacer, database), pageable);
+ RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer, database), pageable);
}
private Page findTargetsBySpec(final Specification spec, final Pageable pageable) {
@@ -384,7 +384,7 @@ public class JpaTargetManagement implements TargetManagement {
final String rsqlParam) {
throwEntityNotFoundIfDsDoesNotExist(distributionSetID);
- final Specification spec = RSQLUtility.parse(rsqlParam, TargetFields.class, virtualPropertyReplacer,
+ final Specification spec = RSQLUtility.buildRsqlSpecification(rsqlParam, TargetFields.class, virtualPropertyReplacer,
database);
return convertPage(
@@ -421,7 +421,7 @@ public class JpaTargetManagement implements TargetManagement {
final String rsqlParam) {
throwEntityNotFoundIfDsDoesNotExist(distributionSetId);
- final Specification spec = RSQLUtility.parse(rsqlParam, TargetFields.class, virtualPropertyReplacer,
+ final Specification spec = RSQLUtility.buildRsqlSpecification(rsqlParam, TargetFields.class, virtualPropertyReplacer,
database);
return convertPage(
@@ -658,7 +658,7 @@ public class JpaTargetManagement implements TargetManagement {
final String targetFilterQuery) {
throwEntityNotFoundIfDsDoesNotExist(distributionSetId);
- final Specification spec = RSQLUtility.parse(targetFilterQuery, TargetFields.class,
+ final Specification spec = RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class,
virtualPropertyReplacer, database);
return findTargetsBySpec(
@@ -673,7 +673,7 @@ public class JpaTargetManagement implements TargetManagement {
public Page findByTargetFilterQueryAndNotInRolloutGroups(final Pageable pageRequest,
final Collection groups, final String targetFilterQuery) {
- final Specification spec = RSQLUtility.parse(targetFilterQuery, TargetFields.class,
+ final Specification spec = RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class,
virtualPropertyReplacer, database);
return findTargetsBySpec((root, cq, cb) -> cb.and(spec.toPredicate(root, cq, cb),
@@ -694,7 +694,7 @@ public class JpaTargetManagement implements TargetManagement {
@Override
public long countByRsqlAndNotInRolloutGroups(final Collection groups, final String targetFilterQuery) {
- final Specification spec = RSQLUtility.parse(targetFilterQuery, TargetFields.class,
+ final Specification spec = RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class,
virtualPropertyReplacer, database);
final List> specList = Arrays.asList(spec,
TargetSpecifications.isNotInRolloutGroups(groups));
@@ -706,7 +706,7 @@ public class JpaTargetManagement implements TargetManagement {
public long countByRsqlAndNonDS(final long distributionSetId, final String targetFilterQuery) {
throwEntityNotFoundIfDsDoesNotExist(distributionSetId);
- final Specification spec = RSQLUtility.parse(targetFilterQuery, TargetFields.class,
+ final Specification spec = RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class,
virtualPropertyReplacer, database);
final List> specList = Lists.newArrayListWithExpectedSize(2);
specList.add(spec);
@@ -750,7 +750,7 @@ public class JpaTargetManagement implements TargetManagement {
throwEntityNotFoundExceptionIfTagDoesNotExist(tagId);
- final Specification spec = RSQLUtility.parse(rsqlParam, TargetFields.class, virtualPropertyReplacer,
+ final Specification spec = RSQLUtility.buildRsqlSpecification(rsqlParam, TargetFields.class, virtualPropertyReplacer,
database);
return convertPage(targetRepository.findAll((Specification) (root, query, cb) -> cb.and(
@@ -763,14 +763,14 @@ public class JpaTargetManagement implements TargetManagement {
final TargetFilterQuery targetFilterQuery = targetFilterQueryRepository.findById(targetFilterQueryId)
.orElseThrow(() -> new EntityNotFoundException(TargetFilterQuery.class, targetFilterQueryId));
- final Specification specs = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class,
+ final Specification specs = RSQLUtility.buildRsqlSpecification(targetFilterQuery.getQuery(), TargetFields.class,
virtualPropertyReplacer, database);
return targetRepository.count(specs);
}
@Override
public long countByRsql(final String targetFilterQuery) {
- final Specification specs = RSQLUtility.parse(targetFilterQuery, TargetFields.class,
+ final Specification specs = RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class,
virtualPropertyReplacer, database);
return targetRepository.count((root, query, cb) -> {
query.distinct(true);
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetTagManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetTagManagement.java
index 3bad1eab3..368e7833d 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetTagManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetTagManagement.java
@@ -106,7 +106,7 @@ public class JpaTargetTagManagement implements TargetTagManagement {
@Override
public Page findByRsql(final Pageable pageable, final String rsqlParam) {
- final Specification spec = RSQLUtility.parse(rsqlParam, TagFields.class, virtualPropertyReplacer,
+ final Specification spec = RSQLUtility.buildRsqlSpecification(rsqlParam, TagFields.class, virtualPropertyReplacer,
database);
return convertTPage(targetTagRepository.findAll(spec, pageable), pageable);
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java
index 750f28ed6..5cf1a5101 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java
@@ -15,8 +15,6 @@ import java.util.concurrent.ScheduledExecutorService;
import javax.persistence.EntityManager;
import javax.sql.DataSource;
-import com.google.common.collect.Maps;
-
import org.eclipse.hawkbit.artifact.repository.ArtifactRepository;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.ControllerManagement;
@@ -30,8 +28,8 @@ import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.RepositoryDefaultConfiguration;
import org.eclipse.hawkbit.repository.RepositoryProperties;
import org.eclipse.hawkbit.repository.RolloutApprovalStrategy;
-import org.eclipse.hawkbit.repository.RolloutGroupManagement;
import org.eclipse.hawkbit.repository.RolloutExecutor;
+import org.eclipse.hawkbit.repository.RolloutGroupManagement;
import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.RolloutStatusCache;
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
@@ -80,6 +78,7 @@ import org.eclipse.hawkbit.repository.jpa.rollout.condition.StartNextGroupRollou
import org.eclipse.hawkbit.repository.jpa.rollout.condition.ThresholdRolloutGroupErrorCondition;
import org.eclipse.hawkbit.repository.jpa.rollout.condition.ThresholdRolloutGroupSuccessCondition;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlParserValidationOracle;
+import org.eclipse.hawkbit.repository.jpa.rsql.DefaultRsqlVisitorFactory;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.Rollout;
@@ -90,6 +89,8 @@ import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
import org.eclipse.hawkbit.repository.model.helper.SystemManagementHolder;
import org.eclipse.hawkbit.repository.model.helper.TenantConfigurationManagementHolder;
import org.eclipse.hawkbit.repository.rsql.RsqlValidationOracle;
+import org.eclipse.hawkbit.repository.rsql.RsqlVisitorFactory;
+import org.eclipse.hawkbit.repository.rsql.RsqlVisitorFactoryHolder;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
import org.eclipse.hawkbit.security.SecurityTokenGenerator;
@@ -127,6 +128,8 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.jta.JtaTransactionManager;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
+import com.google.common.collect.Maps;
+
/**
* General configuration for hawkBit's Repository.
*
@@ -415,7 +418,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/
@Override
@Bean
- public PlatformTransactionManager transactionManager(ObjectProvider transactionManagerCustomizers) {
+ public PlatformTransactionManager transactionManager(
+ ObjectProvider transactionManagerCustomizers) {
return new MultiTenantJpaTransactionManager();
}
@@ -620,15 +624,14 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
@Bean
@ConditionalOnMissingBean
- RolloutExecutor rolloutExecutor(
- final RolloutTargetGroupRepository rolloutTargetGroupRepository, final EntityManager entityManager,
- final RolloutRepository rolloutRepository, final ActionRepository actionRepository,
- final RolloutGroupRepository rolloutGroupRepository, final AfterTransactionCommitExecutor afterCommit,
- final TenantAware tenantAware, final RolloutGroupManagement rolloutGroupManagement,
- final QuotaManagement quotaManagement, final DeploymentManagement deploymentManagement,
- final TargetManagement targetManagement, final EventPublisherHolder eventPublisherHolder,
- final PlatformTransactionManager txManager, final RolloutApprovalStrategy rolloutApprovalStrategy,
- final ApplicationContext context) {
+ RolloutExecutor rolloutExecutor(final RolloutTargetGroupRepository rolloutTargetGroupRepository,
+ final EntityManager entityManager, final RolloutRepository rolloutRepository,
+ final ActionRepository actionRepository, final RolloutGroupRepository rolloutGroupRepository,
+ final AfterTransactionCommitExecutor afterCommit, final TenantAware tenantAware,
+ final RolloutGroupManagement rolloutGroupManagement, final QuotaManagement quotaManagement,
+ final DeploymentManagement deploymentManagement, final TargetManagement targetManagement,
+ final EventPublisherHolder eventPublisherHolder, final PlatformTransactionManager txManager,
+ final RolloutApprovalStrategy rolloutApprovalStrategy, final ApplicationContext context) {
return new JpaRolloutExecutor(rolloutTargetGroupRepository, entityManager, rolloutRepository, actionRepository,
rolloutGroupRepository, afterCommit, tenantAware, rolloutGroupManagement, quotaManagement,
deploymentManagement, targetManagement, eventPublisherHolder, txManager, rolloutApprovalStrategy,
@@ -875,4 +878,26 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
final RolloutManagement rolloutManagement, final SystemSecurityContext systemSecurityContext) {
return new RolloutScheduler(systemManagement, rolloutManagement, systemSecurityContext);
}
+
+ /**
+ * Creates the {@link RsqlVisitorFactory} bean.
+ *
+ * @return A new {@link RsqlVisitorFactory} bean.
+ */
+ @Bean
+ @ConditionalOnMissingBean
+ RsqlVisitorFactory rsqlVisitorFactory() {
+ return new DefaultRsqlVisitorFactory();
+ }
+
+ /**
+ * Obtains the {@link RsqlVisitorFactoryHolder} bean.
+ *
+ * @return The {@link RsqlVisitorFactoryHolder} singleton.
+ */
+ @Bean
+ RsqlVisitorFactoryHolder rsqlVisitorFactoryHolder() {
+ return RsqlVisitorFactoryHolder.getInstance();
+ }
+
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/DefaultRsqlVisitorFactory.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/DefaultRsqlVisitorFactory.java
new file mode 100644
index 000000000..7f49abd1a
--- /dev/null
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/DefaultRsqlVisitorFactory.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2021 Bosch.IO 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 org.eclipse.hawkbit.repository.FieldNameProvider;
+import org.eclipse.hawkbit.repository.rsql.RsqlVisitorFactory;
+
+import cz.jirutka.rsql.parser.ast.RSQLVisitor;
+
+/**
+ * Factory providing {@link RSQLVisitor} instances which validate the nodes
+ * based on a given {@link FieldNameProvider}.
+ */
+public class DefaultRsqlVisitorFactory implements RsqlVisitorFactory {
+
+ @Override
+ public & FieldNameProvider> RSQLVisitor validationRsqlVisitor(
+ final Class fieldNameProvider) {
+ return new FieldValidationRsqlVisitor<>(fieldNameProvider);
+ }
+
+}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/FieldValidationRsqlVisitor.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/FieldValidationRsqlVisitor.java
new file mode 100644
index 000000000..021443749
--- /dev/null
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/FieldValidationRsqlVisitor.java
@@ -0,0 +1,61 @@
+/**
+ * Copyright (c) 2021 Bosch.IO 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 org.eclipse.hawkbit.repository.FieldNameProvider;
+
+import cz.jirutka.rsql.parser.ast.AndNode;
+import cz.jirutka.rsql.parser.ast.ComparisonNode;
+import cz.jirutka.rsql.parser.ast.LogicalNode;
+import cz.jirutka.rsql.parser.ast.OrNode;
+import cz.jirutka.rsql.parser.ast.RSQLVisitor;
+
+/**
+ * {@link RSQLVisitor} implementation which validates the nodes (fields) based
+ * on a given {@link FieldNameProvider} for a given entity type.
+ *
+ * @param
+ * The type the {@link FieldNameProvider} refers to.
+ */
+public class FieldValidationRsqlVisitor & FieldNameProvider> extends AbstractFieldNameRSQLVisitor
+ implements RSQLVisitor {
+
+ /**
+ * Constructs the visitor and initializes it.
+ *
+ * @param fieldNameProvider
+ * The {@link FieldNameProvider} to use for validation.
+ */
+ public FieldValidationRsqlVisitor(final Class fieldNameProvider) {
+ super(fieldNameProvider);
+ }
+
+ @Override
+ public Void visit(final AndNode node, final String param) {
+ return visitNode(node, param);
+ }
+
+ @Override
+ public Void visit(final OrNode node, final String param) {
+ return visitNode(node, param);
+ }
+
+ @Override
+ public Void visit(final ComparisonNode node, final String param) {
+ final A fieldName = getFieldEnumByName(node);
+ getAndValidatePropertyFieldName(fieldName, node);
+ return null;
+ }
+
+ private Void visitNode(final LogicalNode node, final String param) {
+ node.getChildren().forEach(child -> child.accept(this, param));
+ return null;
+ }
+
+}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/JpaQueryRsqlVisitor.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/JpaQueryRsqlVisitor.java
new file mode 100644
index 000000000..eb9871134
--- /dev/null
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/JpaQueryRsqlVisitor.java
@@ -0,0 +1,597 @@
+/**
+ * Copyright (c) 2021 Bosch.IO 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;
+import java.util.Arrays;
+import java.util.Collections;
+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.Map.Entry;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Expression;
+import javax.persistence.criteria.From;
+import javax.persistence.criteria.Join;
+import javax.persistence.criteria.JoinType;
+import javax.persistence.criteria.MapJoin;
+import javax.persistence.criteria.Path;
+import javax.persistence.criteria.PluralJoin;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+import javax.persistence.criteria.Subquery;
+
+import org.apache.commons.lang3.math.NumberUtils;
+import org.eclipse.hawkbit.repository.FieldNameProvider;
+import org.eclipse.hawkbit.repository.FieldValueConverter;
+import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
+import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
+import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.SimpleTypeConverter;
+import org.springframework.beans.TypeMismatchException;
+import org.springframework.orm.jpa.vendor.Database;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import com.google.common.collect.Lists;
+
+import cz.jirutka.rsql.parser.ast.AndNode;
+import cz.jirutka.rsql.parser.ast.ComparisonNode;
+import cz.jirutka.rsql.parser.ast.LogicalNode;
+import cz.jirutka.rsql.parser.ast.Node;
+import cz.jirutka.rsql.parser.ast.OrNode;
+import cz.jirutka.rsql.parser.ast.RSQLVisitor;
+
+/**
+ * An implementation of the {@link RSQLVisitor} to visit the parsed tokens and
+ * build JPA where clauses.
+ *
+ * @param
+ * the enum for providing the field name of the entity field to
+ * filter on.
+ * @param
+ * the entity type referenced by the root
+ */
+public class JpaQueryRsqlVisitor & FieldNameProvider, T> extends AbstractFieldNameRSQLVisitor
+ implements RSQLVisitor, String> {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(JpaQueryRsqlVisitor.class);
+
+ public static final Character LIKE_WILDCARD = '*';
+ private static final char ESCAPE_CHAR = '\\';
+ private static final List NO_JOINS_OPERATOR = Lists.newArrayList("!=", "=out=");
+
+ private final Map>> joinsInLevel = new HashMap<>(3);
+
+ private final CriteriaBuilder cb;
+ private final CriteriaQuery> query;
+ private final Database database;
+ private final Root root;
+ private final SimpleTypeConverter simpleTypeConverter;
+ private final VirtualPropertyReplacer virtualPropertyReplacer;
+
+ private int level;
+ private boolean isOrLevel;
+ private boolean joinsNeeded;
+
+ public JpaQueryRsqlVisitor(final Root root, final CriteriaBuilder cb, final Class enumType,
+ final VirtualPropertyReplacer virtualPropertyReplacer, final Database database,
+ final CriteriaQuery> query) {
+ super(enumType);
+ this.root = root;
+ this.cb = cb;
+ this.query = query;
+ this.virtualPropertyReplacer = virtualPropertyReplacer;
+ this.simpleTypeConverter = new SimpleTypeConverter();
+ this.database = database;
+ this.joinsNeeded = false;
+ }
+
+ private void beginLevel(final boolean isOr) {
+ level++;
+ isOrLevel = isOr;
+ joinsInLevel.put(level, new HashSet<>(2));
+ }
+
+ private void endLevel() {
+ joinsInLevel.remove(level);
+ level--;
+ isOrLevel = false;
+ }
+
+ private Set> getCurrentJoins() {
+ if (level > 0) {
+ return joinsInLevel.get(level);
+ }
+ return Collections.emptySet();
+ }
+
+ private Optional> findCurrentJoinOfType(final Class> type) {
+ return getCurrentJoins().stream().filter(j -> type.equals(j.getJavaType())).findAny();
+ }
+
+ private void addCurrentJoin(final Join