This is an automated email from the ASF dual-hosted git repository. ilgrosso pushed a commit to branch SYNCOPE-1400_SYNCOPE-1401 in repository https://gitbox.apache.org/repos/asf/syncope.git
commit 25dfcc05994b11fe4b33cb8458b2bd57cd93e4d9 Author: Francesco Chicchiriccò <ilgro...@apache.org> AuthorDate: Mon Dec 3 13:18:36 2018 +0100 [SYNCOPE-1400] Adding support for MySQL 8 via OpenJPA 3.0.1 + [SYNCOPE-1401] Adding support for MySQL with JSON type --- core/persistence-jpa-json/pom.xml | 129 ++++++--- ...AJSONAnyDAO.java => AbstractJPAJSONAnyDAO.java} | 81 ++---- .../jpa/dao/AbstractJPAJSONAnySearchDAO.java | 125 +++++++++ .../core/persistence/jpa/dao/MyJPAJSONAnyDAO.java | 120 ++++++++ ...nySearchDAO.java => MyJPAJSONAnySearchDAO.java} | 131 ++------- .../core/persistence/jpa/dao/PGJPAJSONAnyDAO.java | 305 ++------------------- .../persistence/jpa/dao/PGJPAJSONAnySearchDAO.java | 110 +------- .../jpa/entity/JPAJSONEntityFactory.java | 3 +- .../jpa/entity/MyJPAJSONEntityFactory.java | 37 +++ .../main/resources/META-INF/spring-orm-myjson.xml | 137 +++++++++ .../resources/myjson}/domains/Master.properties | 10 +- .../src/main/resources/myjson/indexes.xml | 58 ++++ .../main/resources/myjson/persistence.properties | 23 +- .../src/main/resources/myjson/views.xml | 181 ++++++++++++ .../{pgjsonb => }/domains/MasterContent.xml | 0 .../core/persistence/jpa/dao/JPAAnySearchDAO.java | 4 + .../main/resources/audit/audit_mysql_innodb.sql | 2 +- .../core/persistence/jpa/inner/AnyTypeTest.java | 2 + fit/core-reference/pom.xml | 168 +++++++++++- .../{mysql => myjson}/domains/Master.properties | 10 +- .../{mysql => myjson}/provisioning.properties | 2 +- .../main/resources/mysql/domains/Master.properties | 8 +- .../main/resources/mysql/provisioning.properties | 2 +- pom.xml | 5 +- 24 files changed, 1035 insertions(+), 618 deletions(-) diff --git a/core/persistence-jpa-json/pom.xml b/core/persistence-jpa-json/pom.xml index dfd7bb2..0331a27 100644 --- a/core/persistence-jpa-json/pom.xml +++ b/core/persistence-jpa-json/pom.xml @@ -227,10 +227,6 @@ under the License. <filtering>true</filtering> </testResource> <testResource> - <directory>src/test/resources/pgjsonb</directory> - <filtering>true</filtering> - </testResource> - <testResource> <directory>src/main/resources</directory> <filtering>true</filtering> </testResource> @@ -243,62 +239,119 @@ under the License. </profile> <profile> - <id>sqlgen</id> - - <properties> - <skipTests>true</skipTests> - </properties> + <id>mysql</id> + <dependencies> + <dependency> + <groupId>mysql</groupId> + <artifactId>mysql-connector-java</artifactId> + <version>${jdbc.mysql.version}</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> <defaultGoal>clean verify</defaultGoal> <plugins> <plugin> - <groupId>org.apache.openjpa</groupId> - <artifactId>openjpa-maven-plugin</artifactId> - <inherited>true</inherited> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> <executions> <execution> - <id>sqlgen</id> - <phase>process-classes</phase> + <id>add-test-source</id> + <phase>generate-test-sources</phase> <goals> - <goal>sql</goal> + <goal>add-test-source</goal> </goals> + <configuration> + <sources> + <source>${basedir}/../persistence-jpa/src/test/java</source> + </sources> + </configuration> </execution> </executions> - </plugin> - </plugins> - </build> - </profile> - - <profile> - <id>schemagen</id> - - <properties> - <skipTests>true</skipTests> - </properties> + </plugin> - <build> - <defaultGoal>clean verify</defaultGoal> - - <plugins> <plugin> - <groupId>org.apache.openjpa</groupId> - <artifactId>openjpa-maven-plugin</artifactId> - <inherited>true</inherited> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-failsafe-plugin</artifactId> + <configuration> + <includes> + <include>**/*Test.java</include> + </includes> + <excludedGroups>multitenancy,plainAttrTable</excludedGroups> + </configuration> + </plugin> + + <plugin> + <groupId>io.fabric8</groupId> + <artifactId>docker-maven-plugin</artifactId> + <configuration> + <images> + <image> + <name>mysql/mysql-server:${docker.mysql.version}</name> + <run> + <cmd>--skip-log-bin --server-id=1</cmd> + <env> + <MYSQL_ROOT_PASSWORD>password</MYSQL_ROOT_PASSWORD> + <MYSQL_DATABASE>syncope</MYSQL_DATABASE> + <MYSQL_USER>syncope</MYSQL_USER> + <MYSQL_PASSWORD>syncope</MYSQL_PASSWORD> + </env> + <ports> + <port>3306:3306</port> + </ports> + <wait> + <log>MySQL init process done. Ready for start up.</log> + <time>30000</time> + </wait> + </run> + </image> + </images> + </configuration> <executions> <execution> - <id>schemagen</id> - <phase>process-classes</phase> + <id>start-mysql</id> + <phase>pre-integration-test</phase> + <goals> + <goal>start</goal> + </goals> + </execution> + <execution> + <id>stop-mysql</id> + <phase>post-integration-test</phase> <goals> - <goal>schema</goal> + <goal>stop</goal> + <goal>remove</goal> </goals> </execution> </executions> - </plugin> + </plugin> </plugins> + + <testResources> + <testResource> + <directory>src/test/resources</directory> + <filtering>true</filtering> + </testResource> + <testResource> + <directory>src/main/resources</directory> + <filtering>true</filtering> + </testResource> + <testResource> + <directory>src/main/resources/myjson</directory> + <filtering>true</filtering> + </testResource> + </testResources> </build> </profile> </profiles> - </project> diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnyDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONAnyDAO.java similarity index 81% copy from core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnyDAO.java copy to core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONAnyDAO.java index 94bb72a..f0bca9a 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnyDAO.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONAnyDAO.java @@ -20,40 +20,37 @@ package org.apache.syncope.core.persistence.jpa.dao; import java.io.StringReader; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Set; -import java.util.StringJoiner; import java.util.regex.Pattern; import javax.persistence.Query; import org.apache.commons.jexl3.parser.Parser; import org.apache.commons.jexl3.parser.ParserConstants; import org.apache.commons.jexl3.parser.Token; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.syncope.common.lib.types.AttrSchemaType; import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO; import org.apache.syncope.core.persistence.api.dao.DuplicateException; +import org.apache.syncope.core.persistence.api.dao.JPAJSONAnyDAO; import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; import org.apache.syncope.core.persistence.api.entity.Any; import org.apache.syncope.core.persistence.api.entity.AnyUtils; import org.apache.syncope.core.persistence.api.entity.DerSchema; -import org.apache.syncope.core.persistence.api.entity.PlainAttr; -import org.apache.syncope.core.persistence.api.entity.PlainAttrUniqueValue; +import org.apache.syncope.core.persistence.api.entity.JSONPlainAttr; import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; import org.apache.syncope.core.persistence.api.entity.PlainSchema; import org.apache.syncope.core.persistence.jpa.entity.AbstractEntity; -import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; -import org.apache.syncope.core.persistence.api.entity.JSONPlainAttr; -import org.apache.syncope.core.persistence.api.dao.JPAJSONAnyDAO; -@Repository -public class PGJPAJSONAnyDAO extends AbstractDAO<AbstractEntity> implements JPAJSONAnyDAO { +abstract class AbstractJPAJSONAnyDAO extends AbstractDAO<AbstractEntity> implements JPAJSONAnyDAO { @Autowired private PlainSchemaDAO plainSchemaDAO; @@ -61,22 +58,19 @@ public class PGJPAJSONAnyDAO extends AbstractDAO<AbstractEntity> implements JPAJ @Autowired private DerSchemaDAO derSchemaDAO; - private String queryBegin(final String table) { - return "SELECT DISTINCT id FROM " + table + " u," - + "jsonb_array_elements(u.plainAttrs) attrs," - + "jsonb_array_elements(COALESCE(attrs -> 'values', '[{}]'::jsonb)) attrValues "; - } + protected abstract String queryBegin(String table); - private String attrValueMatch( - final AnyUtils anyUtils, - final PlainSchema schema, - final PlainAttrValue attrValue, - final boolean ignoreCaseMatch) { + protected abstract String attrValueMatch( + AnyUtils anyUtils, + PlainSchema schema, + PlainAttrValue attrValue, + boolean ignoreCaseMatch); + protected Pair<String, Boolean> schemaInfo(final AttrSchemaType schemaType, final boolean ignoreCaseMatch) { String key; boolean lower = false; - switch (schema.getType()) { + switch (schemaType) { case Boolean: key = "booleanValue"; break; @@ -102,29 +96,10 @@ public class PGJPAJSONAnyDAO extends AbstractDAO<AbstractEntity> implements JPAJ key = "stringValue"; } - if (lower) { - return "attrs ->> 'schema' = ? " - + "AND " - + (lower ? "LOWER(" : "") - + (schema.isUniqueConstraint() ? "attrs -> 'uniqueValue'" : "attrValues") + " ->> '" + key - + "'" + (lower ? ")" : "") - + " = " - + (lower ? "LOWER(" : "") - + "?" - + (lower ? ")" : ""); - } else { - PlainAttr<?> container = anyUtils.newPlainAttr(); - container.setSchema(schema); - if (attrValue instanceof PlainAttrUniqueValue) { - container.setUniqueValue((PlainAttrUniqueValue) attrValue); - } else { - ((JSONPlainAttr) container).add(attrValue); - } - return "plainAttrs @> '" + POJOHelper.serialize(Arrays.asList(container)) + "'::jsonb"; - } + return Pair.of(key, lower); } - private <A extends Any<?>> List<A> buildResult(final AnyUtils anyUtils, final List<Object> queryResult) { + protected <A extends Any<?>> List<A> buildResult(final AnyUtils anyUtils, final List<Object> queryResult) { List<A> result = new ArrayList<>(); queryResult.forEach(anyKey -> { A any = anyUtils.<A>dao().find(anyKey.toString()); @@ -210,6 +185,8 @@ public class PGJPAJSONAnyDAO extends AbstractDAO<AbstractEntity> implements JPAJ return attrValues; } + protected abstract List<Object> findByDerAttrValue(String table, Map<String, List<Object>> clauses); + @SuppressWarnings("unchecked") @Transactional(readOnly = true) @Override @@ -272,7 +249,7 @@ public class PGJPAJSONAnyDAO extends AbstractDAO<AbstractEntity> implements JPAJ return Collections.emptyList(); } - StringJoiner clauses = new StringJoiner(" AND id IN "); + Map<String, List<Object>> clauses = new LinkedHashMap<>(); // builder to build the clauses StringBuilder bld = new StringBuilder(); @@ -280,8 +257,6 @@ public class PGJPAJSONAnyDAO extends AbstractDAO<AbstractEntity> implements JPAJ // Contains used identifiers in order to avoid replications Set<String> used = new HashSet<>(); - List<Object> queryParams = new ArrayList<>(); - // Create several clauses: one for eanch identifiers for (int i = 0; i < identifiers.size(); i++) { if (!used.contains(identifiers.get(i))) { @@ -306,25 +281,21 @@ public class PGJPAJSONAnyDAO extends AbstractDAO<AbstractEntity> implements JPAJ append("WHERE "). append(attrValueMatch(anyUtils, schema, attrValue, ignoreCaseMatch)). append(')'); - queryParams.add(schema.getKey()); - queryParams.add(attrValues.get(i)); used.add(identifiers.get(i)); - clauses.add(bld.toString()); + List<Object> queryParams = new ArrayList<>(); + queryParams.add(schema.getKey()); + queryParams.add(attrValues.get(i)); + + clauses.put(bld.toString(), queryParams); } } } LOG.debug("Generated where clauses {}", clauses); - Query query = entityManager().createNativeQuery( - "SELECT DISTINCT id FROM " + table + " u WHERE id IN " + clauses.toString()); - for (int i = 0; i < queryParams.size(); i++) { - query.setParameter(i + 1, queryParams.get(i)); - } - - return buildResult(anyUtils, query.getResultList()); + return buildResult(anyUtils, findByDerAttrValue(table, clauses)); } @Transactional diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONAnySearchDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONAnySearchDAO.java new file mode 100644 index 0000000..693d791 --- /dev/null +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONAnySearchDAO.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.dao; + +import org.apache.commons.lang3.tuple.Pair; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.AttrSchemaType; +import org.apache.syncope.core.persistence.api.dao.search.AttributeCond; + +abstract class AbstractJPAJSONAnySearchDAO extends JPAAnySearchDAO { + + @Override + SearchSupport buildSearchSupport(final AnyTypeKind kind) { + return new SearchSupport(kind); + } + + @Override + protected void processOBS(final SearchSupport svs, final OrderBySupport obs, final StringBuilder where) { + obs.views.forEach(searchView -> { + where.append(','). + append(searchView.name). + append(' ').append(searchView.alias); + }); + } + + protected Pair<String, Boolean> schemaInfo(final AttrSchemaType schemaType, final AttributeCond.Type condType) { + String key; + boolean lower = false; + switch (schemaType) { + case Boolean: + key = "booleanValue"; + break; + + case Date: + key = "dateValue"; + break; + + case Double: + key = "doubleValue"; + break; + + case Long: + key = "longValue"; + break; + + case Binary: + key = "binaryValue"; + break; + + default: + lower = condType == AttributeCond.Type.IEQ || condType == AttributeCond.Type.ILIKE; + key = "stringValue"; + } + + return Pair.of(key, lower); + } + + protected void appendOp(final StringBuilder query, final AttributeCond.Type condType, final boolean not) { + switch (condType) { + case LIKE: + case ILIKE: + if (not) { + query.append("NOT "); + } + query.append(" LIKE "); + break; + + case GE: + if (not) { + query.append('<'); + } else { + query.append(">="); + } + break; + + case GT: + if (not) { + query.append("<="); + } else { + query.append('>'); + } + break; + + case LE: + if (not) { + query.append('>'); + } else { + query.append("<="); + } + break; + + case LT: + if (not) { + query.append(">="); + } else { + query.append('<'); + } + break; + + case EQ: + case IEQ: + default: + if (not) { + query.append('!'); + } + query.append('='); + } + } +} diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAnyDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAnyDAO.java new file mode 100644 index 0000000..e15e690 --- /dev/null +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAnyDAO.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.dao; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; +import javax.persistence.Query; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.core.persistence.api.entity.AnyUtils; +import org.apache.syncope.core.persistence.api.entity.PlainAttr; +import org.apache.syncope.core.persistence.api.entity.PlainAttrUniqueValue; +import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.apache.syncope.core.persistence.api.entity.JSONPlainAttr; + +public class MyJPAJSONAnyDAO extends AbstractJPAJSONAnyDAO { + + @Override + protected String queryBegin(final String table) { + String view = StringUtils.containsIgnoreCase(table, AnyTypeKind.USER.name()) + ? "user_search" + : StringUtils.containsIgnoreCase(table, AnyTypeKind.GROUP.name()) + ? "group_search" + : "anyObject_search"; + return "SELECT DISTINCT id FROM " + view + " "; + } + + @Override + protected String attrValueMatch( + final AnyUtils anyUtils, + final PlainSchema schema, + final PlainAttrValue attrValue, + final boolean ignoreCaseMatch) { + + Pair<String, Boolean> schemaInfo = schemaInfo(schema.getType(), ignoreCaseMatch); + if (schemaInfo.getRight()) { + return "plainSchema = ? " + + "AND " + + (schemaInfo.getRight() ? "LOWER(" : "") + + (schema.isUniqueConstraint() + ? "attrUniqueValue ->> '$." + schemaInfo.getLeft() + "'" + : schemaInfo.getLeft()) + + (schemaInfo.getRight() ? ")" : "") + + " = " + + (schemaInfo.getRight() ? "LOWER(" : "") + + "?" + + (schemaInfo.getRight() ? ")" : ""); + } else { + PlainAttr<?> container = anyUtils.newPlainAttr(); + container.setSchema(schema); + if (attrValue instanceof PlainAttrUniqueValue) { + container.setUniqueValue((PlainAttrUniqueValue) attrValue); + } else { + ((JSONPlainAttr) container).add(attrValue); + } + return "JSON_CONTAINS(plainAttrs, '" + POJOHelper.serialize(Arrays.asList(container)) + "')"; + } + } + + /** + * {@inheritDoc} + * + * This method is a workaround for a bug experienced with MySQL 8.0.13, where the correct implementation (as shown + * in PGJPAJSONAnyDAO.findByDerAttrValue(String, Map<String, List<Object>>)) generates a core dump. + */ + @Override + @SuppressWarnings("unchecked") + protected List<Object> findByDerAttrValue( + final String table, + final Map<String, List<Object>> clauses) { + + if (clauses.isEmpty()) { + return Collections.emptyList(); + } + + Set<Object> result = new HashSet<>(); + AtomicReference<Boolean> first = new AtomicReference<>(Boolean.TRUE); + clauses.forEach((clause, parameters) -> { + Query query = entityManager().createNativeQuery(StringUtils.replaceIgnoreCase(clause, "DISTINCT", "")); + for (int i = 0; i < parameters.size(); i++) { + query.setParameter(i + 1, parameters.get(i)); + } + + Set<Object> local = new HashSet<>(query.getResultList()); + if (first.get()) { + result.addAll(local); + first.set(Boolean.FALSE); + } else { + result.retainAll(local); + } + }); + + return new ArrayList<>(result); + } +} diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAnySearchDAO.java similarity index 55% copy from core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java copy to core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAnySearchDAO.java index f240705..4707372 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAnySearchDAO.java @@ -21,7 +21,6 @@ package org.apache.syncope.core.persistence.jpa.dao; import java.util.Arrays; import java.util.List; import org.apache.commons.lang3.tuple.Pair; -import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.core.persistence.api.dao.search.AttributeCond; import org.apache.syncope.core.persistence.api.dao.search.OrderByClause; import org.apache.syncope.core.persistence.api.entity.AnyUtils; @@ -32,21 +31,7 @@ import org.apache.syncope.core.persistence.api.entity.PlainSchema; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; import org.apache.syncope.core.persistence.api.entity.JSONPlainAttr; -public class PGJPAJSONAnySearchDAO extends JPAAnySearchDAO { - - @Override - SearchSupport buildSearchSupport(final AnyTypeKind kind) { - return new SearchSupport(kind); - } - - @Override - protected void processOBS(final SearchSupport svs, final OrderBySupport obs, final StringBuilder where) { - obs.views.forEach(searchView -> { - where.append(','). - append(searchView.name). - append(' ').append(searchView.alias); - }); - } +public class MyJPAJSONAnySearchDAO extends AbstractJPAJSONAnySearchDAO { @Override protected void parseOrderByForPlainSchema( @@ -62,8 +47,10 @@ public class PGJPAJSONAnySearchDAO extends JPAAnySearchDAO { obs.views.add(svs.field()); - item.select = svs.field().alias + ".attrValues ->> '" + fieldName + "' AS " + fieldName; - item.where = "attrs ->> 'schema' = '" + fieldName + "'"; + item.select = svs.field().alias + "." + + (schema.isUniqueConstraint() ? "attrUniqueValue" : svs.fieldName(schema.getType())) + + " AS " + fieldName; + item.where = "plainSchema = '" + fieldName + "'"; item.orderBy = fieldName + " " + clause.getDirection().name(); } @@ -76,33 +63,7 @@ public class PGJPAJSONAnySearchDAO extends JPAAnySearchDAO { final boolean not, final List<Object> parameters) { - String key; - boolean lower = false; - switch (schema.getType()) { - case Boolean: - key = "booleanValue"; - break; - - case Date: - key = "dateValue"; - break; - - case Double: - key = "doubleValue"; - break; - - case Long: - key = "longValue"; - break; - - case Binary: - key = "binaryValue"; - break; - - default: - lower = cond.getType() == AttributeCond.Type.IEQ || cond.getType() == AttributeCond.Type.ILIKE; - key = "stringValue"; - } + Pair<String, Boolean> schemaInfo = schemaInfo(schema.getType(), cond.getType()); if (!not && cond.getType() == AttributeCond.Type.EQ) { PlainAttr<?> container = anyUtils.newPlainAttr(); @@ -113,71 +74,23 @@ public class PGJPAJSONAnySearchDAO extends JPAAnySearchDAO { ((JSONPlainAttr) container).add(attrValue); } - query.append("plainAttrs @> '"). + query.append("JSON_CONTAINS(plainAttrs, '"). append(POJOHelper.serialize(Arrays.asList(container))). - append("'::jsonb"); + append("')"); } else { - query.append("attrs ->> 'schema' = ?").append(setParameter(parameters, cond.getSchema())). + query.append("plainSchema = ?").append(setParameter(parameters, cond.getSchema())). append(" AND "). - append(lower ? "LOWER(" : ""). + append(schemaInfo.getRight() ? "LOWER(" : ""). append(schema.isUniqueConstraint() - ? "attrs -> 'uniqueValue'" : "attrValues"). - append(" ->> '").append(key).append("'"). - append(lower ? ")" : ""); - - switch (cond.getType()) { - case LIKE: - case ILIKE: - if (not) { - query.append("NOT "); - } - query.append(" LIKE "); - break; - - case GE: - if (not) { - query.append('<'); - } else { - query.append(">="); - } - break; - - case GT: - if (not) { - query.append("<="); - } else { - query.append('>'); - } - break; - - case LE: - if (not) { - query.append('>'); - } else { - query.append("<="); - } - break; - - case LT: - if (not) { - query.append(">="); - } else { - query.append('<'); - } - break; - - case EQ: - case IEQ: - default: - if (not) { - query.append('!'); - } - query.append('='); - } + ? "attrUniqueValue ->> '$." + schemaInfo.getLeft() + "'" + : schemaInfo.getLeft()). + append(schemaInfo.getRight() ? ")" : ""); + + appendOp(query, cond.getType(), not); - query.append(lower ? "LOWER(" : ""). + query.append(schemaInfo.getRight() ? "LOWER(" : ""). append("?").append(setParameter(parameters, cond.getExpression())). - append(lower ? ")" : ""); + append(schemaInfo.getRight() ? ")" : ""); } } @@ -208,17 +121,15 @@ public class PGJPAJSONAnySearchDAO extends JPAAnySearchDAO { append(svs.field().name).append(" WHERE "); switch (cond.getType()) { case ISNOTNULL: - query.append("plainAttrs @> '[{\"schema\":\""). + query.append("JSON_SEARCH(plainAttrs, 'one', '"). append(checked.getLeft().getKey()). - append("\"}]'::jsonb"); + append("', NULL, '$[*].schema') IS NOT NULL"); break; case ISNULL: - query.append("any_id NOT IN ("). - append("SELECT any_id FROM ").append(svs.field().name). - append(" WHERE plainAttrs @> '[{\"schema\":\""). + query.append("JSON_SEARCH(plainAttrs, 'one', '"). append(checked.getLeft().getKey()). - append("\"}]'::jsonb)"); + append("', NULL, '$[*].schema') IS NULL"); break; default: diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnyDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnyDAO.java index 94bb72a..ed1664b 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnyDAO.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnyDAO.java @@ -18,100 +18,49 @@ */ package org.apache.syncope.core.persistence.jpa.dao; -import java.io.StringReader; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.HashSet; import java.util.List; -import java.util.Set; +import java.util.Map; import java.util.StringJoiner; -import java.util.regex.Pattern; import javax.persistence.Query; -import org.apache.commons.jexl3.parser.Parser; -import org.apache.commons.jexl3.parser.ParserConstants; -import org.apache.commons.jexl3.parser.Token; -import org.apache.commons.lang3.StringUtils; -import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO; -import org.apache.syncope.core.persistence.api.dao.DuplicateException; -import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; -import org.apache.syncope.core.persistence.api.entity.Any; +import org.apache.commons.lang3.tuple.Pair; import org.apache.syncope.core.persistence.api.entity.AnyUtils; -import org.apache.syncope.core.persistence.api.entity.DerSchema; import org.apache.syncope.core.persistence.api.entity.PlainAttr; import org.apache.syncope.core.persistence.api.entity.PlainAttrUniqueValue; import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; import org.apache.syncope.core.persistence.api.entity.PlainSchema; -import org.apache.syncope.core.persistence.jpa.entity.AbstractEntity; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; -import org.apache.syncope.core.spring.security.AuthContextUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; import org.apache.syncope.core.persistence.api.entity.JSONPlainAttr; -import org.apache.syncope.core.persistence.api.dao.JPAJSONAnyDAO; -@Repository -public class PGJPAJSONAnyDAO extends AbstractDAO<AbstractEntity> implements JPAJSONAnyDAO { +public class PGJPAJSONAnyDAO extends AbstractJPAJSONAnyDAO { - @Autowired - private PlainSchemaDAO plainSchemaDAO; - - @Autowired - private DerSchemaDAO derSchemaDAO; - - private String queryBegin(final String table) { + @Override + protected String queryBegin(final String table) { return "SELECT DISTINCT id FROM " + table + " u," + "jsonb_array_elements(u.plainAttrs) attrs," + "jsonb_array_elements(COALESCE(attrs -> 'values', '[{}]'::jsonb)) attrValues "; } - private String attrValueMatch( + @Override + protected String attrValueMatch( final AnyUtils anyUtils, final PlainSchema schema, final PlainAttrValue attrValue, final boolean ignoreCaseMatch) { - String key; - boolean lower = false; - - switch (schema.getType()) { - case Boolean: - key = "booleanValue"; - break; - - case Date: - key = "dateValue"; - break; - - case Double: - key = "doubleValue"; - break; - - case Long: - key = "longValue"; - break; - - case Binary: - key = "binaryValue"; - break; - - default: - lower = ignoreCaseMatch; - key = "stringValue"; - } - - if (lower) { + Pair<String, Boolean> schemaInfo = schemaInfo(schema.getType(), ignoreCaseMatch); + if (schemaInfo.getRight()) { return "attrs ->> 'schema' = ? " + "AND " - + (lower ? "LOWER(" : "") - + (schema.isUniqueConstraint() ? "attrs -> 'uniqueValue'" : "attrValues") + " ->> '" + key - + "'" + (lower ? ")" : "") + + (schemaInfo.getRight() ? "LOWER(" : "") + + (schema.isUniqueConstraint() ? "attrs -> 'uniqueValue'" : "attrValues") + + " ->> '" + schemaInfo.getLeft() + + "'" + (schemaInfo.getRight() ? ")" : "") + " = " - + (lower ? "LOWER(" : "") + + (schemaInfo.getRight() ? "LOWER(" : "") + "?" - + (lower ? ")" : ""); + + (schemaInfo.getRight() ? ")" : ""); } else { PlainAttr<?> container = anyUtils.newPlainAttr(); container.setSchema(schema); @@ -124,232 +73,26 @@ public class PGJPAJSONAnyDAO extends AbstractDAO<AbstractEntity> implements JPAJ } } - private <A extends Any<?>> List<A> buildResult(final AnyUtils anyUtils, final List<Object> queryResult) { - List<A> result = new ArrayList<>(); - queryResult.forEach(anyKey -> { - A any = anyUtils.<A>dao().find(anyKey.toString()); - if (any == null) { - LOG.error("Could not find any for key {}", anyKey); - } else { - result.add(any); - } - }); - return result; - } - - @SuppressWarnings("unchecked") - @Transactional(readOnly = true) @Override - public <A extends Any<?>> List<A> findByPlainAttrValue( - final String table, - final AnyUtils anyUtils, - final String schemaKey, - final PlainAttrValue attrValue, - final boolean ignoreCaseMatch) { - - PlainSchema schema = plainSchemaDAO.find(schemaKey); - if (schema == null) { - LOG.error("Invalid schema '{}'", schemaKey); - return Collections.<A>emptyList(); - } - - Query query = entityManager().createNativeQuery( - queryBegin(table) - + "WHERE " + attrValueMatch(anyUtils, schema, attrValue, ignoreCaseMatch)); - query.setParameter(1, schemaKey); - query.setParameter(2, attrValue.getValue()); - - return buildResult(anyUtils, query.getResultList()); - } - - @Transactional(readOnly = true) - @Override - public <A extends Any<?>> A findByPlainAttrUniqueValue( - final String table, - final AnyUtils anyUtils, - final String schemaKey, - final PlainAttrValue attrUniqueValue, - final boolean ignoreCaseMatch) { - - PlainSchema schema = plainSchemaDAO.find(schemaKey); - if (schema == null) { - LOG.error("Invalid schema '{}'", schemaKey); - return null; - } - if (!schema.isUniqueConstraint()) { - LOG.error("This schema has not unique constraint: '{}'", schemaKey); - return null; - } - - List<A> result = findByPlainAttrValue(table, anyUtils, schemaKey, attrUniqueValue, ignoreCaseMatch); - return result.isEmpty() - ? null - : result.get(0); - } - - /** - * Split an attribute value recurring on provided literals/tokens. - * - * @param attrValue value to be split - * @param literals literals/tokens - * @return split value - */ - private List<String> split(final String attrValue, final List<String> literals) { - final List<String> attrValues = new ArrayList<>(); - - if (literals.isEmpty()) { - attrValues.add(attrValue); - } else { - for (String token : attrValue.split(Pattern.quote(literals.get(0)))) { - if (!token.isEmpty()) { - attrValues.addAll(split(token, literals.subList(1, literals.size()))); - } - } - } - - return attrValues; - } - @SuppressWarnings("unchecked") - @Transactional(readOnly = true) - @Override - public <A extends Any<?>> List<A> findByDerAttrValue( + protected List<Object> findByDerAttrValue( final String table, - final AnyUtils anyUtils, - final String schemaKey, - final String value, - final boolean ignoreCaseMatch) { - - DerSchema derSchema = derSchemaDAO.find(schemaKey); - if (derSchema == null) { - LOG.error("Invalid schema '{}'", schemaKey); - return Collections.<A>emptyList(); - } - - Parser parser = new Parser(new StringReader(derSchema.getExpression())); - - // Schema keys - List<String> identifiers = new ArrayList<>(); - - // Literals - List<String> literals = new ArrayList<>(); - - // Get schema keys and literals - for (Token token = parser.getNextToken(); token != null && StringUtils.isNotBlank(token.toString()); - token = parser.getNextToken()) { - - if (token.kind == ParserConstants.STRING_LITERAL) { - literals.add(token.toString().substring(1, token.toString().length() - 1)); - } - - if (token.kind == ParserConstants.IDENTIFIER) { - identifiers.add(token.toString()); - } - } - - // Sort literals in order to process later literals included into others - Collections.sort(literals, (l1, l2) -> { - if (l1 == null && l2 == null) { - return 0; - } else if (l1 != null && l2 == null) { - return -1; - } else if (l1 == null && l2 != null) { - return 1; - } else if (l1.length() == l2.length()) { - return 0; - } else if (l1.length() > l2.length()) { - return -1; - } else { - return 1; - } - }); - - // Split value on provided literals - List<String> attrValues = split(value, literals); - - if (attrValues.size() != identifiers.size()) { - LOG.error("Ambiguous JEXL expression resolution: literals and values have different size"); - return Collections.emptyList(); - } - - StringJoiner clauses = new StringJoiner(" AND id IN "); - - // builder to build the clauses - StringBuilder bld = new StringBuilder(); - - // Contains used identifiers in order to avoid replications - Set<String> used = new HashSet<>(); + final Map<String, List<Object>> clauses) { + StringJoiner actualClauses = new StringJoiner(" AND id IN "); List<Object> queryParams = new ArrayList<>(); - // Create several clauses: one for eanch identifiers - for (int i = 0; i < identifiers.size(); i++) { - if (!used.contains(identifiers.get(i))) { - // verify schema existence and get schema type - PlainSchema schema = plainSchemaDAO.find(identifiers.get(i)); - if (schema == null) { - LOG.error("Invalid schema '{}', ignoring", identifiers.get(i)); - } else { - // clear builder - bld.delete(0, bld.length()); - - PlainAttrValue attrValue; - if (schema.isUniqueConstraint()) { - attrValue = anyUtils.newPlainAttrUniqueValue(); - } else { - attrValue = anyUtils.newPlainAttrValue(); - } - attrValue.setStringValue(attrValues.get(i)); - - bld.append('('). - append(queryBegin(table)). - append("WHERE "). - append(attrValueMatch(anyUtils, schema, attrValue, ignoreCaseMatch)). - append(')'); - queryParams.add(schema.getKey()); - queryParams.add(attrValues.get(i)); - - used.add(identifiers.get(i)); - - clauses.add(bld.toString()); - } - } - } - - LOG.debug("Generated where clauses {}", clauses); + clauses.forEach((clause, parameters) -> { + actualClauses.add(clause); + queryParams.addAll(parameters); + }); Query query = entityManager().createNativeQuery( - "SELECT DISTINCT id FROM " + table + " u WHERE id IN " + clauses.toString()); + "SELECT DISTINCT id FROM " + table + " u WHERE id IN " + actualClauses.toString()); for (int i = 0; i < queryParams.size(); i++) { query.setParameter(i + 1, queryParams.get(i)); } - return buildResult(anyUtils, query.getResultList()); - } - - @Transactional - @Override - public <A extends Any<?>> void checkBeforeSave(final String table, final AnyUtils anyUtils, final A any) { - // check UNIQUE constraints - any.getPlainAttrs().stream(). - filter(attr -> attr.getUniqueValue() != null). - map(JSONPlainAttr.class::cast). - forEach(attr -> { - String schemaKey = attr.getSchemaKey(); - List<A> others = findByPlainAttrValue(table, anyUtils, schemaKey, attr.getUniqueValue(), false); - if (others.isEmpty() || (others.size() == 1 && others.get(0).getKey().equals(any.getKey()))) { - LOG.debug("No duplicate value found for {}", attr.getUniqueValue().getValueAsString()); - } else { - throw new DuplicateException( - "Value " + attr.getUniqueValue().getValueAsString() + " existing for " + schemaKey); - } - }); - - // update sysInfo - as org.apache.syncope.core.persistence.jpa.entity.PlainAttrListener is not invoked - Date now = new Date(); - String username = AuthContextUtils.getUsername(); - LOG.debug("Set last change date '{}' and modifier '{}' for '{}'", now, username, any); - any.setLastModifier(username); - any.setLastChangeDate(now); + return query.getResultList(); } } diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java index f240705..399573a 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java @@ -21,7 +21,6 @@ package org.apache.syncope.core.persistence.jpa.dao; import java.util.Arrays; import java.util.List; import org.apache.commons.lang3.tuple.Pair; -import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.core.persistence.api.dao.search.AttributeCond; import org.apache.syncope.core.persistence.api.dao.search.OrderByClause; import org.apache.syncope.core.persistence.api.entity.AnyUtils; @@ -32,21 +31,7 @@ import org.apache.syncope.core.persistence.api.entity.PlainSchema; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; import org.apache.syncope.core.persistence.api.entity.JSONPlainAttr; -public class PGJPAJSONAnySearchDAO extends JPAAnySearchDAO { - - @Override - SearchSupport buildSearchSupport(final AnyTypeKind kind) { - return new SearchSupport(kind); - } - - @Override - protected void processOBS(final SearchSupport svs, final OrderBySupport obs, final StringBuilder where) { - obs.views.forEach(searchView -> { - where.append(','). - append(searchView.name). - append(' ').append(searchView.alias); - }); - } +public class PGJPAJSONAnySearchDAO extends AbstractJPAJSONAnySearchDAO { @Override protected void parseOrderByForPlainSchema( @@ -76,33 +61,7 @@ public class PGJPAJSONAnySearchDAO extends JPAAnySearchDAO { final boolean not, final List<Object> parameters) { - String key; - boolean lower = false; - switch (schema.getType()) { - case Boolean: - key = "booleanValue"; - break; - - case Date: - key = "dateValue"; - break; - - case Double: - key = "doubleValue"; - break; - - case Long: - key = "longValue"; - break; - - case Binary: - key = "binaryValue"; - break; - - default: - lower = cond.getType() == AttributeCond.Type.IEQ || cond.getType() == AttributeCond.Type.ILIKE; - key = "stringValue"; - } + Pair<String, Boolean> schemaInfo = schemaInfo(schema.getType(), cond.getType()); if (!not && cond.getType() == AttributeCond.Type.EQ) { PlainAttr<?> container = anyUtils.newPlainAttr(); @@ -119,65 +78,16 @@ public class PGJPAJSONAnySearchDAO extends JPAAnySearchDAO { } else { query.append("attrs ->> 'schema' = ?").append(setParameter(parameters, cond.getSchema())). append(" AND "). - append(lower ? "LOWER(" : ""). - append(schema.isUniqueConstraint() - ? "attrs -> 'uniqueValue'" : "attrValues"). - append(" ->> '").append(key).append("'"). - append(lower ? ")" : ""); - - switch (cond.getType()) { - case LIKE: - case ILIKE: - if (not) { - query.append("NOT "); - } - query.append(" LIKE "); - break; - - case GE: - if (not) { - query.append('<'); - } else { - query.append(">="); - } - break; - - case GT: - if (not) { - query.append("<="); - } else { - query.append('>'); - } - break; - - case LE: - if (not) { - query.append('>'); - } else { - query.append("<="); - } - break; - - case LT: - if (not) { - query.append(">="); - } else { - query.append('<'); - } - break; - - case EQ: - case IEQ: - default: - if (not) { - query.append('!'); - } - query.append('='); - } + append(schemaInfo.getRight() ? "LOWER(" : ""). + append(schema.isUniqueConstraint() ? "attrs -> 'uniqueValue'" : "attrValues"). + append(" ->> '").append(schemaInfo.getLeft()).append("'"). + append(schemaInfo.getRight() ? ")" : ""); + + appendOp(query, cond.getType(), not); - query.append(lower ? "LOWER(" : ""). + query.append(schemaInfo.getRight() ? "LOWER(" : ""). append("?").append(setParameter(parameters, cond.getExpression())). - append(lower ? ")" : ""); + append(schemaInfo.getRight() ? ")" : ""); } } diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAJSONEntityFactory.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAJSONEntityFactory.java index bcd046d..a27e5e3 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAJSONEntityFactory.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAJSONEntityFactory.java @@ -140,6 +140,7 @@ public abstract class JPAJSONEntityFactory extends JPAEntityFactory implements I @Override public void afterPropertiesSet() throws Exception { - beanFactory.createBean(jpaJSONAnyDAOClass(), AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false); + beanFactory.registerSingleton("jpaJSONAnyDAO", + beanFactory.createBean(jpaJSONAnyDAOClass(), AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false)); } } diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/MyJPAJSONEntityFactory.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/MyJPAJSONEntityFactory.java new file mode 100644 index 0000000..ca17f7b --- /dev/null +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/MyJPAJSONEntityFactory.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.entity; + +import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; +import org.apache.syncope.core.persistence.api.dao.JPAJSONAnyDAO; +import org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONAnyDAO; +import org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONAnySearchDAO; + +public class MyJPAJSONEntityFactory extends JPAJSONEntityFactory { + + @Override + public Class<? extends AnySearchDAO> anySearchDAOClass() { + return MyJPAJSONAnySearchDAO.class; + } + + @Override + protected Class<? extends JPAJSONAnyDAO> jpaJSONAnyDAOClass() { + return MyJPAJSONAnyDAO.class; + } +} diff --git a/core/persistence-jpa-json/src/main/resources/META-INF/spring-orm-myjson.xml b/core/persistence-jpa-json/src/main/resources/META-INF/spring-orm-myjson.xml new file mode 100644 index 0000000..1c397a0 --- /dev/null +++ b/core/persistence-jpa-json/src/main/resources/META-INF/spring-orm-myjson.xml @@ -0,0 +1,137 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +--> +<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm + http://java.sun.com/xml/ns/persistence/orm_2_0.xsd" + version="2.0"> + + <persistence-unit-metadata> + <persistence-unit-defaults> + <entity-listeners> + <entity-listener class="org.apache.syncope.core.persistence.jpa.validation.entity.EntityValidationListener"> + <pre-persist method-name="validate"/> + <pre-update method-name="validate"/> + </entity-listener> + </entity-listeners> + </persistence-unit-defaults> + </persistence-unit-metadata> + + <entity class="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAJSONAnyObject"> + <attributes> + <basic name="plainAttrs"> + <column column-definition="json"/> + <lob/> + </basic> + </attributes> + </entity> + + <entity class="org.apache.syncope.core.persistence.jpa.entity.group.JPAJSONGroup"> + <attributes> + <basic name="plainAttrs"> + <column column-definition="json"/> + <lob/> + </basic> + </attributes> + </entity> + + <entity class="org.apache.syncope.core.persistence.jpa.entity.user.JPAJSONUser"> + <attributes> + <basic name="plainAttrs"> + <column column-definition="json"/> + <lob/> + </basic> + </attributes> + </entity> + + <entity class="org.apache.syncope.core.persistence.jpa.entity.conf.JPAJSONConf"> + <attributes> + <basic name="plainAttrs"> + <column column-definition="json"/> + <lob/> + </basic> + </attributes> + </entity> + + <entity class="org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup"> + <attributes> + <many-to-one name="userOwner" target-entity="org.apache.syncope.core.persistence.jpa.entity.user.JPAJSONUser"/> + <many-to-one name="groupOwner" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.JPAJSONGroup"/> + </attributes> + </entity> + + <entity class="org.apache.syncope.core.persistence.jpa.entity.group.JPATypeExtension"> + <attributes> + <many-to-one name="group" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.JPAJSONGroup"/> + </attributes> + </entity> + + <entity class="org.apache.syncope.core.persistence.jpa.entity.user.JPAUMembership"> + <attributes> + <many-to-one name="leftEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.user.JPAJSONUser"> + <join-column name="user_id"/> + </many-to-one> + <many-to-one name="rightEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.JPAJSONGroup"> + <join-column name="group_id"/> + </many-to-one> + </attributes> + </entity> + <entity class="org.apache.syncope.core.persistence.jpa.entity.user.JPAUDynGroupMembership"> + <attributes> + <one-to-one name="group" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.JPAJSONGroup"/> + </attributes> + </entity> + <entity class="org.apache.syncope.core.persistence.jpa.entity.user.JPAURelationship"> + <attributes> + <many-to-one name="leftEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.user.JPAJSONUser"> + <join-column name="user_id"/> + </many-to-one> + <many-to-one name="rightEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAJSONAnyObject"> + <join-column name="anyObject_id"/> + </many-to-one> + </attributes> + </entity> + + <entity class="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAMembership"> + <attributes> + <many-to-one name="leftEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAJSONAnyObject"> + <join-column name="anyObject_id"/> + </many-to-one> + <many-to-one name="rightEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.JPAJSONGroup"> + <join-column name="group_id"/> + </many-to-one> + </attributes> + </entity> + <entity class="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAADynGroupMembership"> + <attributes> + <one-to-one name="group" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.JPAJSONGroup"/> + </attributes> + </entity> + <entity class="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAARelationship"> + <attributes> + <many-to-one name="leftEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAJSONAnyObject"> + <join-column name="left_anyObject_id"/> + </many-to-one> + <many-to-one name="rightEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAJSONAnyObject"> + <join-column name="right_anyObject_id"/> + </many-to-one> + </attributes> + </entity> +</entity-mappings> diff --git a/fit/core-reference/src/main/resources/mysql/domains/Master.properties b/core/persistence-jpa-json/src/main/resources/myjson/domains/Master.properties similarity index 76% copy from fit/core-reference/src/main/resources/mysql/domains/Master.properties copy to core/persistence-jpa-json/src/main/resources/myjson/domains/Master.properties index ce09eec..d945b5a 100644 --- a/fit/core-reference/src/main/resources/mysql/domains/Master.properties +++ b/core/persistence-jpa-json/src/main/resources/myjson/domains/Master.properties @@ -14,15 +14,15 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -Master.driverClassName=com.mysql.jdbc.Driver -Master.url=jdbc:mysql://localhost:3306/syncope?characterEncoding=UTF-8&relaxAutoCommit=true +Master.driverClassName=com.mysql.cj.jdbc.Driver +Master.url=jdbc:mysql://localhost:3306/syncope?useSSL=false&allowPublicKeyRetrieval=true&characterEncoding=UTF-8 Master.schema= Master.username=syncope Master.password=syncope -Master.databasePlatform=org.apache.openjpa.jdbc.sql.MySQLDictionary(blobTypeName=LONGBLOB) -Master.orm=META-INF/spring-orm.xml +Master.databasePlatform=org.apache.openjpa.jdbc.sql.MySQLDictionary(blobTypeName=LONGBLOB,timestampTypeName=DATETIME(3)) +Master.orm=META-INF/spring-orm-myjson.xml Master.pool.maxActive=10 Master.pool.minIdle=2 -Master.audit.sql=audit.sql +Master.audit.sql=audit_mysql_innodb.sql diff --git a/core/persistence-jpa-json/src/main/resources/myjson/indexes.xml b/core/persistence-jpa-json/src/main/resources/myjson/indexes.xml new file mode 100644 index 0000000..b16004d --- /dev/null +++ b/core/persistence-jpa-json/src/main/resources/myjson/indexes.xml @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +--> +<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> +<properties> + <comment>Additional indexes (in respect to JPA's)</comment> + + <entry key="UDynGroupMembers_any_id">CREATE INDEX UDynGroupMembers_any_id ON UDynGroupMembers(any_id)</entry> + <entry key="UDynGroupMembers_group_id">CREATE INDEX UDynGroupMembers_group_id ON UDynGroupMembers(group_id)</entry> + <entry key="ADynGroupMembers_any_id">CREATE INDEX ADynGroupMembers_any_id ON ADynGroupMembers(any_id)</entry> + <entry key="ADynGroupMembers_group_id">CREATE INDEX ADynGroupMembers_group_id ON ADynGroupMembers(group_id)</entry> + + <entry key="DynRoleMembers_any_id">CREATE INDEX DynRoleMembers_any_id ON DynRoleMembers(any_id)</entry> + <entry key="DynRoleMembers_role_id">CREATE INDEX DynRoleMembers_role_id ON DynRoleMembers(role_id)</entry> + + <entry key="DynRealmMembers_any_id">CREATE INDEX DynRealmMembers_any_id ON DynRealmMembers(any_id)</entry> + <entry key="DynRealmMembers_realm_id">CREATE INDEX DynRealmMembers_dynRealm_id ON DynRealmMembers(dynRealm_id)</entry> + + <entry key="CPlainAttrValue_stringvalueIndex">CREATE INDEX CAttrValue_stringvalueIndex ON CPlainAttrValue(stringvalue)</entry> + <entry key="CPlainAttrValue_datevalueIndex">CREATE INDEX CAttrValue_datevalueIndex ON CPlainAttrValue(datevalue)</entry> + <entry key="CPlainAttrValue_longvalueIndex">CREATE INDEX CAttrValue_longvalueIndex ON CPlainAttrValue(longvalue)</entry> + <entry key="CPlainAttrValue_doublevalueIndex">CREATE INDEX CAttrValue_doublevalueIndex ON CPlainAttrValue(doublevalue)</entry> + <entry key="CPlainAttrValue_booleanvalueIndex">CREATE INDEX CAttrValue_booleanvalueIndex ON CPlainAttrValue(booleanvalue)</entry> + + <entry key="UMembership_GroupIndex">CREATE INDEX UMembership_GroupIndex ON UMembership(group_id)</entry> + <entry key="UMembership_UserIndex">CREATE INDEX UMembership_UserIndex ON UMembership(user_id)</entry> + <entry key="AMembership_GroupIndex">CREATE INDEX AMembership_GroupIndex ON AMembership(group_id)</entry> + <entry key="AMembership_AnyObjectIndex">CREATE INDEX AMembership_AnyObjectIndex ON AMembership(anyObject_id)</entry> + + <entry key="URelationship_RightIndex">CREATE INDEX URelationship_RightIndex ON URelationship(anyObject_id)</entry> + <entry key="URelationship_LeftIndex">CREATE INDEX URelationship_LeftIndex ON URelationship(user_id)</entry> + <entry key="ARelationship_RightIndex">CREATE INDEX ARelationship_RightIndex ON ARelationship(right_anyObject_id)</entry> + <entry key="ARelationship_AnyObjectIndex">CREATE INDEX ARelationship_AnyObjectIndex ON ARelationship(left_anyObject_id)</entry> + + <entry key="CPlainAttrValue_attrIndex">CREATE INDEX CPlainAttrValue_attrIndex on CPlainAttrValue(attribute_id)</entry> + <entry key="CPAttrUniqueValue_attrIndex">CREATE INDEX CPAttrUniqueValue_attrIndex on CPlainAttrUniqueValue(attribute_id)</entry> + + <entry key="CPlainAttr_owner_Index">CREATE INDEX CPlainAttr_owner_Index on CPlainAttr(owner_id)</entry> + <entry key="CPlainAttr_schema_Index">CREATE INDEX CPlainAttr_schema_Index on CPlainAttr(schema_id)</entry> + + <entry key="Task_executedIndex">CREATE INDEX Task_executedIndex ON Task(executed)</entry> +</properties> diff --git a/fit/core-reference/src/main/resources/mysql/domains/Master.properties b/core/persistence-jpa-json/src/main/resources/myjson/persistence.properties similarity index 50% copy from fit/core-reference/src/main/resources/mysql/domains/Master.properties copy to core/persistence-jpa-json/src/main/resources/myjson/persistence.properties index ce09eec..31a2dd8 100644 --- a/fit/core-reference/src/main/resources/mysql/domains/Master.properties +++ b/core/persistence-jpa-json/src/main/resources/myjson/persistence.properties @@ -14,15 +14,14 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -Master.driverClassName=com.mysql.jdbc.Driver -Master.url=jdbc:mysql://localhost:3306/syncope?characterEncoding=UTF-8&relaxAutoCommit=true -Master.schema= -Master.username=syncope -Master.password=syncope -Master.databasePlatform=org.apache.openjpa.jdbc.sql.MySQLDictionary(blobTypeName=LONGBLOB) -Master.orm=META-INF/spring-orm.xml - -Master.pool.maxActive=10 -Master.pool.minIdle=2 - -Master.audit.sql=audit.sql +content.directory=${conf.directory} +entity.factory=org.apache.syncope.core.persistence.jpa.entity.MyJPAJSONEntityFactory +plainSchema.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONPlainSchemaDAO +plainAttr.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONPlainAttrDAO +plainAttrValue.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONPlainAttrValueDAO +any.search.dao=org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONAnySearchDAO +user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONUserDAO +group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONGroupDAO +anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONAnyObjectDAO +conf.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONConfDAO +openjpa.RemoteCommitProvider=sjvm diff --git a/core/persistence-jpa-json/src/main/resources/myjson/views.xml b/core/persistence-jpa-json/src/main/resources/myjson/views.xml new file mode 100644 index 0000000..aa0e3c0 --- /dev/null +++ b/core/persistence-jpa-json/src/main/resources/myjson/views.xml @@ -0,0 +1,181 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +--> +<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> +<properties> + + <entry key="UDynGroupMembers"> + CREATE TABLE UDynGroupMembers( + any_id CHAR(36), + group_id CHAR(36), + UNIQUE(any_id, group_id)) + </entry> + <entry key="ADynGroupMembers"> + CREATE TABLE ADynGroupMembers( + anyType_id VARCHAR(255), + any_id CHAR(36), + group_id CHAR(36), + UNIQUE(anyType_id, any_id, group_id)) + </entry> + <entry key="DynRoleMembers"> + CREATE TABLE DynRoleMembers( + any_id CHAR(36), + role_id VARCHAR(255), + UNIQUE(any_id, role_id)) + </entry> + <entry key="DynRealmMembers"> + CREATE TABLE DynRealmMembers( + any_id CHAR(36), + dynRealm_id VARCHAR(255), + UNIQUE(any_id, dynRealm_id)) + </entry> + + <!-- user --> + <entry key="user_search"> + CREATE VIEW user_search AS + + SELECT u.id as any_id, u.*, attrs.* + FROM SyncopeUser u, JSON_TABLE(COALESCE(plainAttrs, '[{}]'), '$[*]' COLUMNS ( + plainSchema VARCHAR(255) PATH '$.schema', + NESTED PATH '$.values[*]' COLUMNS ( + binaryValue LONGBLOB PATH '$.binaryValue', + booleanValue INT PATH '$.booleanValue', + dateValue BIGINT(20) PATH '$.dateValue', + doubleValue DOUBLE PATH '$.doubleValue', + longValue BIGINT(20) PATH '$.longValue', + stringValue VARCHAR(255) PATH '$.stringValue'), + attrUniqueValue JSON PATH '$.uniqueValue') + ) AS attrs + </entry> + <entry key="user_search_urelationship"> + CREATE VIEW user_search_urelationship AS + + SELECT m.user_id AS any_id, m.anyObject_id AS right_any_id, m.type_id AS type + FROM URelationship m + </entry> + <entry key="user_search_umembership"> + CREATE VIEW user_search_umembership AS + + SELECT m.user_id AS any_id, g.id AS group_id, g.name AS group_name + FROM UMembership m, SyncopeGroup g + WHERE m.group_id = g.id + </entry> + <entry key="user_search_role"> + CREATE VIEW user_search_role AS + + SELECT ss.user_id AS any_id, ss.role_id AS role_id + FROM SyncopeUser_SyncopeRole ss + </entry> + <entry key="user_search_priv"> + CREATE VIEW user_search_priv AS + + SELECT ss.user_id AS any_id, sp.privilege_id AS privilege_id + FROM SyncopeUser_SyncopeRole ss, SyncopeRole_Privilege sp + WHERE ss.role_id = sp.role_id + </entry> + <entry key="user_search_dynpriv"> + CREATE VIEW user_search_dynpriv AS + + SELECT any_id, privilege_id + FROM DynRoleMembers drm, SyncopeRole_Privilege rp + WHERE drm.role_id = rp.role_id + </entry> + <entry key="user_search_resource"> + CREATE VIEW user_search_resource AS + + SELECT st.user_id AS any_id, st.resource_id AS resource_id + FROM SyncopeUser_ExternalResource st + </entry> + <entry key="user_search_group_res"> + CREATE VIEW user_search_group_res AS + + SELECT m.user_id AS any_id, st.resource_id AS resource_id + FROM UMembership m, SyncopeGroup r, SyncopeGroup_ExternalResource st + WHERE m.group_id = r.id AND st.group_id = r.id + </entry> + + <!-- anyObject --> + <entry key="anyObject_search"> + CREATE VIEW anyObject_search AS + + SELECT a.id as any_id, a.*, attrs.* + FROM AnyObject a, JSON_TABLE(COALESCE(plainAttrs, '[{}]'), '$[*]' COLUMNS ( + plainSchema VARCHAR(255) PATH '$.schema', + NESTED PATH '$.values[*]' COLUMNS ( + binaryValue LONGBLOB PATH '$.binaryValue', + booleanValue INT PATH '$.booleanValue', + dateValue BIGINT(20) PATH '$.dateValue', + doubleValue DOUBLE PATH '$.doubleValue', + longValue BIGINT(20) PATH '$.longValue', + stringValue VARCHAR(255) PATH '$.stringValue'), + attrUniqueValue JSON PATH '$.uniqueValue') + ) AS attrs + </entry> + <entry key="anyObject_search_arelationship"> + CREATE VIEW anyObject_search_arelationship AS + + SELECT m.left_anyObject_id AS any_id, m.right_anyObject_id AS right_any_id, m.type_id AS type + FROM ARelationship m + </entry> + <entry key="anyObject_search_amembership"> + CREATE VIEW anyObject_search_amembership AS + + SELECT m.anyObject_id AS any_id, g.id AS group_id, g.name AS group_name + FROM AMembership m, SyncopeGroup g + WHERE m.group_id = g.id + </entry> + <entry key="anyObject_search_resource"> + CREATE VIEW anyObject_search_resource AS + + SELECT st.anyObject_id AS any_id, st.resource_id AS resource_id + FROM AnyObject_ExternalResource st + </entry> + <entry key="anyObject_search_group_res"> + CREATE VIEW anyObject_search_group_res AS + + SELECT m.anyObject_id AS any_id, st.resource_id AS resource_id + FROM AMembership m, SyncopeGroup r, SyncopeGroup_ExternalResource st + WHERE m.group_id = r.id AND st.group_id = r.id + </entry> + + <!-- group --> + <entry key="group_search"> + CREATE VIEW group_search AS + + SELECT g.id as any_id, g.*, attrs.* + FROM SyncopeGroup g, JSON_TABLE(COALESCE(plainAttrs, '[{}]'), '$[*]' COLUMNS ( + plainSchema VARCHAR(255) PATH '$.schema', + NESTED PATH '$.values[*]' COLUMNS ( + binaryValue LONGBLOB PATH '$.binaryValue', + booleanValue INT PATH '$.booleanValue', + dateValue BIGINT(20) PATH '$.dateValue', + doubleValue DOUBLE PATH '$.doubleValue', + longValue BIGINT(20) PATH '$.longValue', + stringValue VARCHAR(255) PATH '$.stringValue'), + attrUniqueValue JSON PATH '$.uniqueValue') + ) AS attrs + </entry> + <entry key="group_search_resource"> + CREATE VIEW group_search_resource AS + + SELECT st.group_id AS any_id, st.resource_id AS resource_id + FROM SyncopeGroup_ExternalResource st + </entry> + +</properties> diff --git a/core/persistence-jpa-json/src/test/resources/pgjsonb/domains/MasterContent.xml b/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml similarity index 100% rename from core/persistence-jpa-json/src/test/resources/pgjsonb/domains/MasterContent.xml rename to core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java index 02e2535..fcf3bba 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java @@ -166,6 +166,8 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO { // 1. get the query string from the search condition StringBuilder queryString = getQuery(buildEffectiveCond(cond, filter.getRight()), parameters, svs); + LOG.debug("Query: {}, parameters: {}", queryString, parameters); + // 2. take into account realms and ordering OrderBySupport obs = parseOrderBy(kind, svs, orderBy); if (queryString.charAt(0) == '(') { @@ -179,6 +181,8 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO { append(filter.getLeft()). append(buildOrderBy(obs)); + LOG.debug("Query with auth and order by statements: {}, parameters: {}", queryString, parameters); + // 3. prepare the search query Query query = entityManager().createNativeQuery(queryString.toString()); diff --git a/core/persistence-jpa/src/main/resources/audit/audit_mysql_innodb.sql b/core/persistence-jpa/src/main/resources/audit/audit_mysql_innodb.sql index ff753fa..4d8426c 100644 --- a/core/persistence-jpa/src/main/resources/audit/audit_mysql_innodb.sql +++ b/core/persistence-jpa/src/main/resources/audit/audit_mysql_innodb.sql @@ -21,4 +21,4 @@ CREATE TABLE IF NOT EXISTS SYNCOPEAUDIT ( LOGGER VARCHAR(255) NOT NULL, MESSAGE TEXT NOT NULL, THROWABLE TEXT -) ENGINE=InnoDB +) ENGINE=InnoDB; diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyTypeTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyTypeTest.java index 1db62d8..26b1da9 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyTypeTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyTypeTest.java @@ -90,6 +90,7 @@ public class AnyTypeTest extends AbstractTest { newType.setKey("new type"); newType.setKind(AnyTypeKind.USER); anyTypeDAO.save(newType); + entityManager().flush(); }); } @@ -100,6 +101,7 @@ public class AnyTypeTest extends AbstractTest { newType.setKey("group"); newType.setKind(AnyTypeKind.ANY_OBJECT); anyTypeDAO.save(newType); + entityManager().flush(); }); } diff --git a/fit/core-reference/pom.xml b/fit/core-reference/pom.xml index 85c992c..645dcca 100644 --- a/fit/core-reference/pom.xml +++ b/fit/core-reference/pom.xml @@ -724,7 +724,7 @@ under the License. <filtering>true</filtering> </resource> <resource> - <directory>${basedir}/../../core/persistence-jpa-json/src/test/resources/pgjsonb/domains</directory> + <directory>${basedir}/../../core/persistence-jpa-json/src/test/resources/domains</directory> <targetPath>${project.build.directory}/classes/domains</targetPath> <filtering>true</filtering> </resource> @@ -851,7 +851,165 @@ under the License. </testResources> </build> </profile> + + <profile> + <id>myjson-it</id> + + <properties> + <jdbcdriver.groupId>mysql</jdbcdriver.groupId> + <jdbcdriver.artifactId>mysql-connector-java</jdbcdriver.artifactId> + </properties> + + <dependencies> + <dependency> + <groupId>org.apache.syncope.core</groupId> + <artifactId>syncope-core-persistence-jpa-json</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>mysql</groupId> + <artifactId>mysql-connector-java</artifactId> + <version>${jdbc.mysql.version}</version> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <defaultGoal>clean verify</defaultGoal> + + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-war-plugin</artifactId> + <configuration> + <packagingExcludes>WEB-INF/classes/domains/Two*</packagingExcludes> + </configuration> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-antrun-plugin</artifactId> + <inherited>true</inherited> + <executions> + <execution> + <id>remove-domain-Two</id> + <phase>prepare-package</phase> + <configuration> + <target> + <delete> + <fileset dir="${project.build.directory}/classes/domains" includes="Two*"/> + </delete> + </target> + </configuration> + <goals> + <goal>run</goal> + </goals> + </execution> + </executions> + </plugin> + + <plugin> + <groupId>io.fabric8</groupId> + <artifactId>docker-maven-plugin</artifactId> + <configuration> + <images> + <image> + <name>mysql/mysql-server:${docker.mysql.version}</name> + <run> + <cmd>--skip-log-bin --server-id=1</cmd> + <env> + <MYSQL_ROOT_PASSWORD>password</MYSQL_ROOT_PASSWORD> + <MYSQL_DATABASE>syncope</MYSQL_DATABASE> + <MYSQL_USER>syncope</MYSQL_USER> + <MYSQL_PASSWORD>syncope</MYSQL_PASSWORD> + </env> + <ports> + <port>3306:3306</port> + </ports> + <wait> + <log>MySQL init process done. Ready for start up.</log> + <time>30000</time> + </wait> + </run> + </image> + </images> + </configuration> + <executions> + <execution> + <id>start-mysql</id> + <phase>pre-integration-test</phase> + <goals> + <goal>start</goal> + </goals> + </execution> + <execution> + <id>stop-mysql</id> + <phase>post-integration-test</phase> + <goals> + <goal>stop</goal> + <goal>remove</goal> + </goals> + </execution> + </executions> + </plugin> + + <plugin> + <groupId>org.codehaus.cargo</groupId> + <artifactId>cargo-maven2-plugin</artifactId> + <inherited>true</inherited> + <executions> + <execution> + <id>start-container</id> + <phase>pre-integration-test</phase> + <goals> + <goal>start</goal> + </goals> + </execution> + <execution> + <id>stop-container</id> + <phase>post-integration-test</phase> + <goals> + <goal>stop</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + + <resources> + <resource> + <directory>src/main/resources</directory> + <filtering>true</filtering> + <excludes> + <exclude>provisioning.properties</exclude> + <exclude>indexes.xml</exclude> + <exclude>views.xml</exclude> + </excludes> + </resource> + <resource> + <directory>src/main/resources/myjson</directory> + <filtering>true</filtering> + </resource> + <resource> + <directory>${basedir}/../../core/persistence-jpa-json/src/test/resources/domains</directory> + <targetPath>${project.build.directory}/classes/domains</targetPath> + <filtering>true</filtering> + </resource> + </resources> + <testResources> + <testResource> + <directory>${basedir}/../../core/persistence-jpa-json/src/main/resources/myjson</directory> + <includes> + <include>persistence.properties</include> + <include>indexes.xml</include> + <include>views.xml</include> + </includes> + </testResource> + </testResources> + </build> + </profile> + <profile> <id>mysql-it</id> @@ -879,9 +1037,11 @@ under the License. <configuration> <images> <image> - <name>mysql/mysql-server:5.7</name> + <name>mysql/mysql-server:${docker.mysql.version}</name> <run> + <cmd>--skip-log-bin --server-id=1</cmd> <env> + <MYSQL_ROOT_PASSWORD>password</MYSQL_ROOT_PASSWORD> <MYSQL_DATABASE>syncope</MYSQL_DATABASE> <MYSQL_USER>syncope</MYSQL_USER> <MYSQL_PASSWORD>syncope</MYSQL_PASSWORD> @@ -889,6 +1049,10 @@ under the License. <ports> <port>3306:3306</port> </ports> + <wait> + <log>MySQL init process done. Ready for start up.</log> + <time>30000</time> + </wait> </run> </image> </images> diff --git a/fit/core-reference/src/main/resources/mysql/domains/Master.properties b/fit/core-reference/src/main/resources/myjson/domains/Master.properties similarity index 76% copy from fit/core-reference/src/main/resources/mysql/domains/Master.properties copy to fit/core-reference/src/main/resources/myjson/domains/Master.properties index ce09eec..d945b5a 100644 --- a/fit/core-reference/src/main/resources/mysql/domains/Master.properties +++ b/fit/core-reference/src/main/resources/myjson/domains/Master.properties @@ -14,15 +14,15 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -Master.driverClassName=com.mysql.jdbc.Driver -Master.url=jdbc:mysql://localhost:3306/syncope?characterEncoding=UTF-8&relaxAutoCommit=true +Master.driverClassName=com.mysql.cj.jdbc.Driver +Master.url=jdbc:mysql://localhost:3306/syncope?useSSL=false&allowPublicKeyRetrieval=true&characterEncoding=UTF-8 Master.schema= Master.username=syncope Master.password=syncope -Master.databasePlatform=org.apache.openjpa.jdbc.sql.MySQLDictionary(blobTypeName=LONGBLOB) -Master.orm=META-INF/spring-orm.xml +Master.databasePlatform=org.apache.openjpa.jdbc.sql.MySQLDictionary(blobTypeName=LONGBLOB,timestampTypeName=DATETIME(3)) +Master.orm=META-INF/spring-orm-myjson.xml Master.pool.maxActive=10 Master.pool.minIdle=2 -Master.audit.sql=audit.sql +Master.audit.sql=audit_mysql_innodb.sql diff --git a/fit/core-reference/src/main/resources/mysql/provisioning.properties b/fit/core-reference/src/main/resources/myjson/provisioning.properties similarity index 97% copy from fit/core-reference/src/main/resources/mysql/provisioning.properties copy to fit/core-reference/src/main/resources/myjson/provisioning.properties index 48f9ef6..3883cb5 100644 --- a/fit/core-reference/src/main/resources/mysql/provisioning.properties +++ b/fit/core-reference/src/main/resources/myjson/provisioning.properties @@ -27,6 +27,6 @@ anyObjectProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultAn virAttrCache=org.apache.syncope.core.provisioning.java.cache.MemoryVirAttrCache quartz.jobstore=org.quartz.impl.jdbcjobstore.StdJDBCDelegate -quartz.sql=tables_mysql.sql +quartz.sql=tables_mysql_innodb.sql quartz.scheduler.idleWaitTime=5000 quartz.disableInstance=false diff --git a/fit/core-reference/src/main/resources/mysql/domains/Master.properties b/fit/core-reference/src/main/resources/mysql/domains/Master.properties index ce09eec..e36e370 100644 --- a/fit/core-reference/src/main/resources/mysql/domains/Master.properties +++ b/fit/core-reference/src/main/resources/mysql/domains/Master.properties @@ -14,15 +14,15 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -Master.driverClassName=com.mysql.jdbc.Driver -Master.url=jdbc:mysql://localhost:3306/syncope?characterEncoding=UTF-8&relaxAutoCommit=true +Master.driverClassName=com.mysql.cj.jdbc.Driver +Master.url=jdbc:mysql://localhost:3306/syncope?useSSL=false&allowPublicKeyRetrieval=true&characterEncoding=UTF-8 Master.schema= Master.username=syncope Master.password=syncope -Master.databasePlatform=org.apache.openjpa.jdbc.sql.MySQLDictionary(blobTypeName=LONGBLOB) +Master.databasePlatform=org.apache.openjpa.jdbc.sql.MySQLDictionary(blobTypeName=LONGBLOB,timestampTypeName=DATETIME(3)) Master.orm=META-INF/spring-orm.xml Master.pool.maxActive=10 Master.pool.minIdle=2 -Master.audit.sql=audit.sql +Master.audit.sql=audit_mysql_innodb.sql diff --git a/fit/core-reference/src/main/resources/mysql/provisioning.properties b/fit/core-reference/src/main/resources/mysql/provisioning.properties index 48f9ef6..3883cb5 100644 --- a/fit/core-reference/src/main/resources/mysql/provisioning.properties +++ b/fit/core-reference/src/main/resources/mysql/provisioning.properties @@ -27,6 +27,6 @@ anyObjectProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultAn virAttrCache=org.apache.syncope.core.provisioning.java.cache.MemoryVirAttrCache quartz.jobstore=org.quartz.impl.jdbcjobstore.StdJDBCDelegate -quartz.sql=tables_mysql.sql +quartz.sql=tables_mysql_innodb.sql quartz.scheduler.idleWaitTime=5000 quartz.disableInstance=false diff --git a/pom.xml b/pom.xml index 25df74d..5293fff 100644 --- a/pom.xml +++ b/pom.xml @@ -396,7 +396,7 @@ under the License. <spring.version>5.1.3.RELEASE</spring.version> <spring-security.version>5.1.2.RELEASE</spring-security.version> - <openjpa.version>3.0.0</openjpa.version> + <openjpa.version>3.0.1-SNAPSHOT</openjpa.version> <hikaricp.version>3.2.0</hikaricp.version> <bval.version>1.1.2</bval.version> @@ -518,10 +518,11 @@ under the License. <protractor.version>5.4.0</protractor.version> <docker.postgresql.version>11.1</docker.postgresql.version> + <docker.mysql.version>8.0</docker.mysql.version> <docker.mariadb.version>10.4</docker.mariadb.version> <jdbc.postgresql.version>42.2.5</jdbc.postgresql.version> - <jdbc.mysql.version>5.1.47</jdbc.mysql.version> + <jdbc.mysql.version>8.0.13</jdbc.mysql.version> <jdbc.mariadb.version>2.3.0</jdbc.mariadb.version> <jdbc.mssql.version>6.4.0.jre</jdbc.mssql.version>