Skip to content

Commit

Permalink
[SYNCOPE-1662] Introducing JSON support for MariaDB (#797)
Browse files Browse the repository at this point in the history
  • Loading branch information
ilgrosso authored Jul 26, 2024
1 parent 21ac757 commit 78e28a7
Show file tree
Hide file tree
Showing 29 changed files with 1,558 additions and 77 deletions.
28 changes: 28 additions & 0 deletions .github/workflows/mariadb.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,31 @@ jobs:
run: mvn -U -T 1C -P 'skipTests,all'
- name: 'Integration Tests: MariaDB'
run: mvn -f fit/core-reference/pom.xml -P mariadb-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true

majson:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Java JDK
uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: 11
- name: Setup Maven
uses: stCarolas/setup-maven@v5
with:
maven-version: 3.9.6
- uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- name: Build
run: mvn -U -T 1C -P 'skipTests,all'
- name: 'Unit Tests: MariaDB JPA JSON'
run: mvn -f core/persistence-jpa-json/pom.xml -P majson -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
- name: 'Integration Tests: MariaDB JPA JSON'
run: mvn -f fit/core-reference/pom.xml -P majson-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
116 changes: 115 additions & 1 deletion core/persistence-jpa-json/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ under the License.
<!-- possible values: sql | schema -->
<action>sql</action>

<!-- possible values: pgjsonb | myjson | ojson -->
<!-- possible values: pgjsonb | myjson | majson | ojson -->
<orm>ojson</orm>

<skipTests>true</skipTests>
Expand Down Expand Up @@ -422,6 +422,120 @@ under the License.
</build>
</profile>

<profile>
<id>majson</id>

<dependencies>
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>${jdbc.mariadb.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<defaultGoal>clean verify</defaultGoal>

<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>add-test-source</id>
<phase>generate-test-sources</phase>
<goals>
<goal>add-test-source</goal>
</goals>
<configuration>
<sources>
<source>${basedir}/../persistence-jpa/src/test/java</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>

<plugin>
<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>
<systemPropertyVariables>
<profileId>${project.activeProfiles[0].id}</profileId>
<CORE_PROPERTIES>classpath:core-majson.properties,classpath:core-majson-test.properties</CORE_PROPERTIES>
<DB_CONTAINER_IP>${docker.container.mariadb.ip}</DB_CONTAINER_IP>
<syncope.connid.location>file:${bundles.directory}/</syncope.connid.location>
</systemPropertyVariables>
</configuration>
</plugin>

<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<configuration>
<images>
<image>
<alias>mariadb</alias>
<name>mariadb:${docker.mariadb.version}</name>
<run>
<env>
<MYSQL_ROOT_PASSWORD>password</MYSQL_ROOT_PASSWORD>
<MYSQL_DATABASE>syncope</MYSQL_DATABASE>
<MYSQL_USER>syncope</MYSQL_USER>
<MYSQL_PASSWORD>syncope</MYSQL_PASSWORD>
</env>
<tmpfs>
<mount>/var/lib/mysql:rw</mount>
</tmpfs>
<wait>
<log>MariaDB init process done. Ready for start up.</log>
<time>30000</time>
</wait>
</run>
</image>
</images>
</configuration>
<executions>
<execution>
<id>start-mariadb</id>
<phase>pre-integration-test</phase>
<goals>
<goal>start</goal>
</goals>
</execution>
<execution>
<id>stop-mariadb</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop</goal>
<goal>remove</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>

<testResources>
<testResource>
<directory>src/test/resources</directory>
<filtering>true</filtering>
</testResource>
</testResources>
</build>
</profile>

<profile>
<id>ojson</id>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* 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;

import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager;
import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
import org.apache.syncope.core.persistence.api.dao.AuditConfDAO;
import org.apache.syncope.core.persistence.api.dao.DynRealmDAO;
import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
import org.apache.syncope.core.persistence.api.dao.GroupDAO;
import org.apache.syncope.core.persistence.api.dao.JPAJSONAnyDAO;
import org.apache.syncope.core.persistence.api.dao.PlainAttrDAO;
import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
import org.apache.syncope.core.persistence.api.dao.RealmDAO;
import org.apache.syncope.core.persistence.api.dao.UserDAO;
import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
import org.apache.syncope.core.persistence.api.entity.EntityFactory;
import org.apache.syncope.core.persistence.jpa.dao.MaJPAJSONAnyDAO;
import org.apache.syncope.core.persistence.jpa.dao.MaJPAJSONAnySearchDAO;
import org.apache.syncope.core.persistence.jpa.dao.MaJPAJSONAuditConfDAO;
import org.apache.syncope.core.persistence.jpa.dao.MaJPAJSONPlainSchemaDAO;
import org.apache.syncope.core.persistence.jpa.entity.MaJPAJSONEntityFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Lazy;

@ConditionalOnExpression("#{'${provisioning.quartz.sql}' matches '.*mariadb.*'}")
public class MaJPAJSONPersistenceContext extends JPAJSONPersistenceContext {

@ConditionalOnMissingBean(name = "maJPAJSONEntityFactory")
@Bean
public EntityFactory entityFactory() {
return new MaJPAJSONEntityFactory();
}

@ConditionalOnMissingBean(name = "maJPAJSONAnyDAO")
@Bean
public JPAJSONAnyDAO anyDAO(final @Lazy PlainSchemaDAO plainSchemaDAO) {
return new MaJPAJSONAnyDAO(plainSchemaDAO);
}

@ConditionalOnMissingBean(name = "maJPAJSONAnySearchDAO")
@Bean
public AnySearchDAO anySearchDAO(
final @Lazy RealmDAO realmDAO,
final @Lazy DynRealmDAO dynRealmDAO,
final @Lazy UserDAO userDAO,
final @Lazy GroupDAO groupDAO,
final @Lazy AnyObjectDAO anyObjectDAO,
final @Lazy PlainSchemaDAO schemaDAO,
final @Lazy EntityFactory entityFactory,
final AnyUtilsFactory anyUtilsFactory,
final PlainAttrValidationManager validator) {

return new MaJPAJSONAnySearchDAO(
realmDAO,
dynRealmDAO,
userDAO,
groupDAO,
anyObjectDAO,
schemaDAO,
entityFactory,
anyUtilsFactory,
validator);
}

@ConditionalOnMissingBean(name = "maJPAJSONAuditConfDAO")
@Bean
public AuditConfDAO auditConfDAO() {
return new MaJPAJSONAuditConfDAO();
}

@ConditionalOnMissingBean(name = "maJPAJSONPlainSchemaDAO")
@Bean
public PlainSchemaDAO plainSchemaDAO(
final AnyUtilsFactory anyUtilsFactory,
final @Lazy PlainAttrDAO plainAttrDAO,
final @Lazy ExternalResourceDAO resourceDAO) {

return new MaJPAJSONPlainSchemaDAO(anyUtilsFactory, plainAttrDAO, resourceDAO);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,29 @@

abstract class AbstractJPAJSONAnyDAO extends AbstractDAO<AbstractEntity> implements JPAJSONAnyDAO {

/**
* Split an attribute value recurring on provided literals/tokens.
*
* @param attrValue value to be split
* @param literals literals/tokens
* @return split value
*/
private static List<String> split(final String attrValue, final List<String> literals) {
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;
}

protected final PlainSchemaDAO plainSchemaDAO;

protected AbstractJPAJSONAnyDAO(final PlainSchemaDAO plainSchemaDAO) {
Expand Down Expand Up @@ -129,6 +152,20 @@ protected <A extends Any<?>> List<A> buildResult(final AnyUtils anyUtils, final
return result;
}

protected String plainAttrQuery(
final String table,
final AnyUtils anyUtils,
final PlainSchema schema,
final PlainAttrValue attrValue,
final boolean ignoreCaseMatch,
final List<Object> queryParams) {

queryParams.add(schema.getKey());
queryParams.add(getAttrValue(schema, attrValue, ignoreCaseMatch));

return queryBegin(table) + "WHERE " + attrValueMatch(anyUtils, schema, attrValue, ignoreCaseMatch);
}

@SuppressWarnings("unchecked")
@Transactional(readOnly = true)
@Override
Expand All @@ -144,11 +181,12 @@ public <A extends Any<?>> List<A> findByPlainAttrValue(
return List.of();
}

List<Object> queryParams = new ArrayList<>();
Query query = entityManager().createNativeQuery(
queryBegin(table)
+ "WHERE " + attrValueMatch(anyUtils, schema, attrValue, ignoreCaseMatch));
query.setParameter(1, schema.getKey());
query.setParameter(2, getAttrValue(schema, attrValue, ignoreCaseMatch));
plainAttrQuery(table, anyUtils, schema, attrValue, ignoreCaseMatch, queryParams));
for (int i = 0; i < queryParams.size(); i++) {
query.setParameter(i + 1, queryParams.get(i));
}

return buildResult(anyUtils, query.getResultList());
}
Expand Down Expand Up @@ -177,31 +215,8 @@ public <A extends Any<?>> Optional<A> findByPlainAttrUniqueValue(
: Optional.of(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
*/
protected List<String> split(final String attrValue, final List<String> literals) {
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")
protected List<Object> findByDerAttrValue(
private List<Object> findByDerAttrValue(
final String table,
final Map<String, List<Object>> clauses) {

Expand Down Expand Up @@ -302,26 +317,18 @@ public <A extends Any<?>> List<A> findByDerAttrValue(
// clear builder
bld.delete(0, bld.length());

PlainAttrValue attrValue;
if (schema.isUniqueConstraint()) {
attrValue = anyUtils.newPlainAttrUniqueValue();
} else {
attrValue = anyUtils.newPlainAttrValue();
}
PlainAttrValue attrValue = schema.isUniqueConstraint()
? anyUtils.newPlainAttrUniqueValue()
: anyUtils.newPlainAttrValue();
attrValue.setStringValue(attrValues.get(i));

List<Object> queryParams = new ArrayList<>();
bld.append('(').
append(queryBegin(table)).
append("WHERE ").
append(attrValueMatch(anyUtils, schema, attrValue, ignoreCaseMatch)).
append(plainAttrQuery(table, anyUtils, schema, attrValue, ignoreCaseMatch, queryParams)).
append(')');

used.add(identifiers.get(i));

List<Object> queryParams = new ArrayList<>();
queryParams.add(schema.getKey());
queryParams.add(getAttrValue(schema, attrValue, ignoreCaseMatch));

clauses.put(bld.toString(), queryParams);
}
}
Expand Down
Loading

0 comments on commit 78e28a7

Please sign in to comment.