Author Archives: Nicolas Riousset

About Nicolas Riousset

Président et fondateur de NeoLegal, développe des solutions logicielles qui facilitent le quotidien des professionnels du droit des sociétés.

Release Failed when publishing to Sonatype

While trying to publish a new version of my fork of mysql-backup4j, the release of the latest version failed with error “Event: Failed: Repository Writable”, whether I tried to publish using “mvn deploy” (whatever the value of the autoReleaseAfterClose setting), or from the nexus repository manager portal.

The maven “mvn deploy” command returned the following error

[ERROR]
[ERROR] Nexus Staging Rules Failure Report
[ERROR] ==================================
[ERROR]
[ERROR] Repository "frneolegal-1040" failures
[ERROR]   Rule "RepositoryWritePolicy" failures
[ERROR]     * Artifact updating: Repository ='releases:Releases' does not allow updating artifact='/fr/neolegal/mysql-backup4j/1.2.3/mysql-backup4j-1.2.3.jar'

While the Nexus Repository manager displayed this one

Once identified, the issue was kind of obvious. My maven “target” folder hadn’t been cleaned prior to the compilation, and the artefacts of the previous version were still present. The deploy operation was pushing the new and old artefacts, triggering a release failure, since the previous version had already been released.

Deleting the “target” folder manually – or even better using the “mvn clean” command – solved the issue. And to prevent any future occurence, just use the following maven command :

mvn clean deploy

How to create email folders in Microsoft Office 365 Groups ?

Let’s say you have a Ms Office 365 “Support” group, whose emails are managed by your tech support. You need to create emails folders and rules to organize your Support mailbox. Yet, nowhere is a menu to Create these folders in Outlook.

It came to me as a surprise, but this feature is not supported by default. However, as a Ms 365 admin, you can partially enable it, provided that your users rely only on Outlook Web Access (OWA), and not on the Outlook desktop application, because the group folders will on be visible on the web version of Outlook.

Microsoft documented the procedure here. In brief, you’ll have to use PowerShell to :

  1. Enable folders and rules creation, using :
    Set-OrganizationConfig -IsGroupFoldersAndRulesEnabled $true
  2. Authorize folders and rules creation, using :
    Set-OrganizationConfig -IsGroupMemberAllowedToEditContent $true

But if you are Ms 365 newbie, like me, you may have to first setup your powershell environment. Here is the complete – summarized – sequence :

  1. Ensure your Ms Office 365 account is an admin account
  2. On your Windows Machine, run PowerShell as an administrator, so that you’ll be able to Install the Exchange Online Powershell module (as documented by Microsoft here), and run the following commands.
  3. Install-Module -Name PSWSMan
  4. Install-Module -Name ExchangeOnlineManagement
  5. Update-Module -Name ExchangeOnlineManagement
  6. Import-Module ExchangeOnlineManagement;
  7. Connect-ExchangeOnline -UserPrincipalName <your_user_name>
  8. Set-OrganizationConfig -IsGroupFoldersAndRulesEnabled $true
  9. Set-OrganizationConfig -IsGroupMemberAllowedToEditContent $true

Once the configuration modified, there’s a delay for the change to be taken into account. You may immediately see the “Create subfolder” menu appear when right clicking on your group in OWA, yet you may get a “Permission denied” error for a while. However, after half an hour, I was able to create and view Group folders, but only in OWA, as documented by Microsoft.

Case sensitivity issue with liquibase change log table names

While starting a SpringBoot/JHipster MariaDB application, I ran into this Liquibase database update error:

liquibase.exception.MigrationFailedException: Migration failed for changeset config/liquibase/changelog/00000000000000_initial_schema.xml

This was not a fresh database initialization, but an upgrade of an existing databse wich was recently moved to a new MariaDB server. Yet the error pointed to the initialization changeset.

The issue came from the liquibase change log tables, named DATABASECHANGELOG and DATABASECHANGELOGLOCK, uppercase. Our database naming convention required lower case table names, hence the conversion to lower case of the liquibase change log table names. On the previous MariaDB Server, table names where case insensitive, this was not the case anymore on the new server, leading to the creation by Liquibase of two empty DATABASECHANGELOG and DATABASECHANGELOGLOCK tables, next to the databasechangelog and databasechangeloglock existing ones. Consequence: Liquibase was trying to initialize an already initialized database.

Here are three ways to solve this problem

Option 1 : Switch back to Liquibase default change log table names

That may not be elegant, but switching back to the default Liquibase change log table names will fix the issue, and prevent similar future issues. For example, when using SpringBoot, I had to rename the tables in two places : in the SpringBoot “application.yml” file, and in the Maven “pom.xml” file. That wouldn’t have been required would I have stuck to the default table names.

Option 2 : Disable case sensitivity for MariaDB table names

Changing the database configuration ensures that you won’t encounter the issue with any Liquibase application. However, when ever you change server you’ll have to enable this setting, and this may be an issue in the unlikely case where a database has two tables with the same name but different case.

The MariaDB ‘lower_case_table_names’ setting in my.cnf enables table name case insensitivity, by forcing all table names to be lowercase. To check if it’s enabled, use :

SHOW GLOBAL VARIABLES LIKE ‘lower_case_table_names’;

SHOW GLOBAL VARIABLES LIKE 'lower_case_table_names';

If ‘lower_case_table_names’ = 0, follow this procedure to change the parameter value in the MariaDB configuration files :

[mariadb]
lower_case_table_names=1

Option 3 : Specify Liquibase change log table names in SpringBoot application.properties file

Eventually, you can specify your Liquibase change log table names in your application.properties or application.yml file, as explained here, using the spring.liquibase.database-change-log-lock-table and spring.liquibase.database-change-log-table properties.

For example, in an application.yml file :

spring:
  liquibase:
    database-change-log-lock-table: 'databasechangeloglock'
    database-change-log-table: 'databasechangelog'

Gérer les mises à jour de sa base données SQL avec Liquibase

Dans les années 2000, pour résoudre des problèmes de mise à jour de base de données avec des scripts SQL à appliquer manuellement, dans un ordre précis, pour un type de base de données spécifique, en fonction d’une version de départ variable, j’ai développé un outil en C++ qui automatisait tout ce processus. Et ce qui devait être un outil développé rapidement a été beaucoup plus complexe que prévu.

Quelques années plus tard, société et application différentes mais même problématique. Cette fois-ci, c’est en Delphi que j’ai traité le problème, plus rapidement que la première fois, mais moins rapidement que ce que j’aurais imaginé.

Un problème rencontré deux fois en quelques années signifie qu’il est probablement commun. Et les problèmes communs ont toujours une solution standardisée, qui évite de réinventer la roue. En l’occurrence, cette solution s’appelle Liquibase.

Liquibase résout tous les problèmes auxquels on ne pense pas forcément quand on commence à écrire un petit script de mise à jour de la BD. Quelle sera la version du moteur SQL cible ? Est ce que je dois supporter plusieurs moteurs SQL ? Quelle sera la version initiale de la base de données à mettre à jour ? Comment gérer la migration des données ? Que faire en cas d’erreur pendant la mise à jour ? Comment identifier et fusionner les différences entre des évolutions concurrentes de la BD ? Etc.

Toutes ces réponses apportées à des questions auxquelle on n’a pas encore pensé quand on envisage de traiter le problème manuellement justifient la learning curve pour s’approprier Liquibase, en particulier l’organisation et le format des fichiers qui rendent les fichiers de mise à jour indépendants de la base de données cible.

Heureusement, il y a une forte communauté autour de l’outil, et des documentations claires et précises. En l’occurrence, il s’agissait d’adopter Liquibase pour un projet existant. Liquibase a une procédure pour cette situtation spécifique, ce qui ne dispense pas de lire au moins la présentation générale du fonctionnement de Liquibase

Mon environnement de dev s’appuie sur Maven, JAVA, SpringBoot. L’adoption de Liquibase se faisait dans un contexte plus général de migration de l’application vers le modèle JHipster. J’avais donc des scripts d’initialisation de la base de données générés pour le projet de demo JHipster.

Première étape, configurer le plugin Liquibase dans le POM.xml pour pointer vers la base de données à utiliser, et pour indiquer le package contenant toutes les entités :

<plugin>
	<groupId>org.liquibase</groupId>
	<artifactId>liquibase-maven-plugin</artifactId>
	<version>${liquibase.version}</version>
	<configuration>
		<changeLogFile>${project.basedir}/src/main/resources/config/liquibase/master.xml</changeLogFile>
		<outputChangeLogFile>${project.basedir}/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml</outputChangeLogFile>
		<diffChangeLogFile>${project.basedir}/src/main/resources/config/liquibase/changelog/${maven.build.timestamp}_changelog.xml</diffChangeLogFile>
		<driver>org.mariadb.jdbc.Driver</driver>
		<url>jdbc:mariadb://localhost:3310/bartleby</url>
		<defaultSchemaName></defaultSchemaName>
		<username>myUser</username>
		<password>myPassword</password>
		<referenceUrl>hibernate:spring:com.riousset.bartleby.persistence?dialect=org.hibernate.dialect.MariaDB103Dialect&amp;hibernate.physical_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy&amp;hibernate.implicit_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy</referenceUrl>
		<verbose>true</verbose>
		<logging>debug</logging>
		<contexts>!test</contexts>
	</configuration>
        ...

Les points clés : les propriétés driver, url, username, password concernent la base de données à mettre à jour. La propriété referenceUrl concerne le modèle de référence, en fonction duquel la BD devra être mise à jour. Dans l’exemple ci-dessous, je pointe vers le package contenant les entités JPA / Hibernate de mon application.

Seconde étape, générer le script d’initialisation de la base de données en fonction du schéma existant. Le changeLog sera généré dans le fichier indiqué par la propriété outputChangeLogFile :

./mvnw liquibase:generateChangeLog -Pprod

Troisième étape, maintenant que le changelog d’initialisation a été créé, il est possible d’initialiser un base de données vierge. Mais la mise à jour d’une base de données préexistante causerait des conflits. Liquibase offre la commande changeLogSync pour indiquer qu’un changeLog a déjà été exécuté sur un base. C’est un outil “bête”: changeLogSync ne vérifie pas que les scripts ont réellement été exécutés, il se contente d’indiquer dans la table DATABASECHANGELOG que tous les changeSet ont bien été exécutés :

./mvnw liquibase:changelogSync -Pprod -D"liquibase.changeLogFile=config/liquibase/changelog/00000000000000_initial_schema.xml"

Quatrième étape, on peut créer ou modifier des entités JPA, et laisser liquibase générer les scripts de mise à jour :

./mvnw liquibase:diff

Le fichier généré doit alors être ajouté au fichier master.xml. par exemple :

<?xml version="1.0" encoding="utf-8"?>
<databaseChangeLog
    xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
    <property name="now" value="now()" dbms="h2"/>
    <property name="floatType" value="float4" dbms="h2"/>
    <property name="uuidType" value="varchar(36)" dbms="h2"/>
    <property name="datetimeType" value="datetime" dbms="h2"/>
    <property name="clobType" value="clob" dbms="h2"/>
    <property name="blobType" value="blob" dbms="h2"/>
    <property name="now" value="now()" dbms="mariadb"/>
    <property name="floatType" value="float" dbms="mariadb"/>
    <property name="clobType" value="clob" dbms="mariadb"/>
    <property name="blobType" value="longblob" dbms="mariadb"/>
    <property name="uuidType" value="varchar(36)" dbms="mariadb"/>
    <property name="datetimeType" value="datetime(6)" dbms="mariadb"/>

    <include file="config/liquibase/changelog/00000000000000_initial_schema.xml" relativeToChangelogFile="false"/>
	<include file="config/liquibase/changelog/20221004092650_changelog.xml" relativeToChangelogFile="false"/>
    <!-- jhipster-needle-liquibase-add-changelog - JHipster will add liquibase changelogs here -->
    <!-- jhipster-needle-liquibase-add-constraints-changelog - JHipster will add liquibase constraints changelogs here -->
    <!-- jhipster-needle-liquibase-add-incremental-changelog - JHipster will add incremental liquibase changelogs here -->
</databaseChangeLog>

Enfin, l’application des changements à une base de données se fait avec la commande :

./mvnw liquibase:update -Pprod