An example leibnix artifact to know what to upload to maven central

Web github URL: https://github.com/axeldamian/bibliotecanueva

First of all, dependence

<dependency>
 <groupId>com.leibnix</groupId>
 <artifactId>newlibrary</artifactId>
 <version>1.2</version>
</dependency>


You can see it here:

https://central.sonatype.com/artifact/com.leibnix/newlibrary/1.2

https://mvnrepository.com/artifact/com.leibnix/newlibrary/1.2

bibliotecanueva

Creating a new library in Java with Maven.

It’s kind of cumbersome to upload to Maven Central, but it’s configured only once. I crashed at each step and had to send emails to Maven Central and get very annoying.

First of all, think of a unique groupId, because it’s the Maven namespace that identifies you worldwide. If it’s not valid in Maven, you’ll have to think of another one and modify it in your entire project.

As origen is the remote repository instead of origin

git pull origen main

origen is origin in Spanish

Create it with the command

 mvn archetype:generate -DgroupId=com.newlibrary -DartifactId=newlibrary -Dversion=1.0-SNAPSHOT -DarchetypeArtifactId=maven-archetype-quickstart

Replace “com.newlibrary” with your groupId and “nwelibrary” with your artifactId.

Run in a terminal, standing where the pom is:

mvn clean package
mvn install

The project jar is created:

Captura de pantalla 2024-12-08 a la(s) 18 42 42

And the .m2/repository/com folder contains our project

Obviously .m2 is invisible and is located in ~

On Mac you can see it by pressing command + shift + .

Captura de pantalla 2024-12-08 a la(s) 18 37 51

Once it is in .m2/repository it can be used by adding it to the pom:
within dependencies in another project:

    <dependency>
            <groupId>com.newlibrary</groupId>
            <artifactId>newlibrary</artifactId>
            <version>1.0</version>
        </dependency>

to use it, for example:

package services;

import org.springframework.stereotype.Service;

import com.newlibrary.clases.Operations;

@Service
public class ExampleService {

    public int sumar(){
        Operations op = new Operations();
        return op.sum(1, 2);
    }

}

If the bean is not recognized:

import services.ExampleService;

@Configuration
public class AppConfig {

    @Bean
    public ExampleService exampleService() {
        return new ExampleService();
    }

}
Captura de pantalla 2024-12-08 a la(s) 20 34 16

And use it, for example, in a controller:

package com.leibnix.leibnix.controllers;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import services.ExampleService;

@RestController
public class BibliotecaExternaController {

    Logger log = LogManager.getLogger(this.getClass().getSimpleName());

    @Autowired
    ExampleService exampleService;

    @GetMapping("/suma")
    public int libreriaExterna() {
        log.info("call to endpoint /suma");
        return exampleService.sumar();
    }

}
Captura de pantalla 2024-12-08 a la(s) 20 37 07

when performing a GET to /suma

Captura de pantalla 2024-12-08 a la(s) 20 24 53

Publish project to maven central

There are 15 tags that must be included

They are metadata, that is, additional information that accompanies the code.

For example, a book has its content, which would be the code, and its author, which is metadata.

No dice hacer tests, yo hice con Junit, si no haces tests tu biblioteca no puede ser tomada en serio.

number of required tags: 15
within:

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
     ...HERE IT GOES...
</project>

groupId —- root of pom.xml, inside project, example com.example

artifactId —- root of pom.xml, inside project, project name, goes inside the groupId hierarchically in maven central, has to be unique in the group

version —- root of pom.xml, inside project, example 1.0.0, another example 0.0.1-SNAPSHOT, if it is a snapshot, you cannot upload it to maven central, it is to indicate that the version is not productive.

packaging: jar, war —- root of pom.xml, inside project, jar is more common than war

name —- root of pom.xml, inside project

description —- root of pom.xml, inside project

url —- root of pom.xml, inside project

licenses — root of the pom.xml, inside project, the project license. It must be an approved OSI license
example:

 <licenses>
    <license>
      <name>MIT License</name>
      <url>https://opensource.org/licenses/MIT</url>
    </license>
  </licenses>

scm — root of the pom.xml, inside project, the project’s version control information (for example, Git),
svn or others.
example of use with github:

<scm>
  <connection>scm:git:https://github.com/usuario/proyecto.git</connection>
<developerConnection>scm:git:ssh://github.com/usuario/proyecto.git</developerConnection>

Example of empty tags:

 <scm />

  <repositories>
    <!-- There are no repositories configured -->
  </repositories>

  <pluginRepositories>
    <!-- There are no plugin repositories configured -->
  </pluginRepositories>

  <dependencies>
    <!-- Project Dependencies -->
  </dependencies>

  <build>
    <plugins>
      <!-- Project Plugins -->
    </plugins>

Sign files with GPG

The documentation says PGP (not free) but GPG (free, a copy of PGP) serves the same purpose.

It is mandatory to sign: they are all generated in /target, the project jar, the jar that has the source code, the javadoc jar, the pom in target which is a copy of the original pom.

GPG is a terminal command program, on Mac it is installed with Homebrew.
I don’t want to make a tutorial about gpg, the only thing I say is that it was born from pgp and is called GnuPG, gnu privacy guard, it was made by Werner Koch and it has a GPL license.


Signature with gpg or asc extension.
The gpg passphrase is not in the pom, but in settings.xml, it can be in the pom.xml but it is not correct.

Keep reading, I’ll explain settings.xml in a bit.

  <servers>
<!-- to sign with GPG -->
    <server>
      <id>gpg.passphrase</id>
      <passphrase>GPG PASSPHRASE</passphrase>
    </server>
  </servers>

To sign with .asc instead of .gpg:

gpg -s --armor file.txt

generate public and private key, for that gpg asks for a passphrase, which is a password but longer

gpg --gen-key

sign a file with gpg

gpg --sign mi-artifact.jar

Verify the signature of a file using the corresponding signature file.

gpg --verify file.txt.asc

gpg encrypts messages using RSA, an algorithm that uses large prime numbers, the message is a signature “First Name Last Name Email”

You can sign by hand or use a plugin in java maven and it signs everything automatically.

The goal is to automate everything, not do anything manually.

Requirements for publishing to maven central

Releases :Only releases can be uploaded to the Central Repository, i.e. files that will not change and only depend on other files already released and available in the repository.

javadoc and sources: for search in IDE,

PGP Signature,

Minimum POM information : There are some requirements for minimum information in the POMs found in the Central Repository.

coordinates : It is important to choose the right coordinates for your project. In particular regarding groupId and domain ownership.

source: https://maven.apache.org/repository/guide-central-repository-upload.html

A command to do everything together can be

mvn clean package javadoc:jar verify

If the commands are executed separately, it throws an error. When signing, you have to make sure that you first create a package, on the same line.

Plugin to create a jar from sources

generate newlibrary-1.0-sources.jar inside /target

        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-source-plugin</artifactId>
          <version>3.2.1</version>
          <executions>
            <execution>
              <id>attach-sources</id>
              <goals>
                <goal>jar</goal>
              </goals>
            </execution>
          </executions>
        </plugin>

GPG plugin for maven to automatically sign files needed by maven central

It is executed in the “verify” phase to sign within /target: the .pom which is a copy of the original .pom, the javadoc jar, the documentation, the application jar and the sources.jar which contains the sources, there are 4 signatures, doing them manually with gpg is possible, but it is annoying, the code:

        <!-- gpg sign -->
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-gpg-plugin</artifactId>
          <version>3.2.7</version>
          <executions>
            <execution>
              <id>sign-files-for-maven-central</id>
              <phase>verify</phase>
              <goals>
                <goal>sign</goal>
              </goals>
              <configuration>
                  <showFiles>true</showFiles>
                  <displayFingerprint>true</displayFingerprint>
              </configuration>
            </execution>
          </executions>
        </plugin>

It should be noted that the javadoc jar and sources.jar cannot be executed because they do not have Main-Class in MANIFEST.mf contained in the jar.

Plugin to generate jar from sources

Don’t put anything that isn’t attached.

            <configuration>
                <attach>false</attach>
                <!-- ... -->
            </configuration>

If it is not attached GPG does not sign it, by default it is true, so if you do not set that option, its value is true.

Example:

        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-source-plugin</artifactId>
          <version>3.2.1</version>
          <executions>
            <execution>
              <id>attach-sources</id>
              <goals>
                <goal>jar</goal>
              </goals>
            </execution>
          </executions>
        </plugin>

configuration is at the same level as goals.

settings.xml file

It goes in ~/.m2/settings.xml

If it is not created, create it.

It is a global configuration file for all java maven projects.

The passwords of the plugins that need them are included, you are not going to put the password in the pom, they can see it from github.

There are also global repositories that all maven projects use and global properties.

For example GPG uses passphrase, so the GPG plugin will need the passphrase, it goes in settings.xml not in the project’s pom.xml.

You can also put the passphrase in an environment variable or a protected environment variable and the project’s pom.xml uses the environment variable

Change where it says GPG PASSPHRASE to the actual passphrase:

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
          http://maven.apache.org/xsd/settings-1.0.0.xsd">

  <profiles>

      <!-- Define a variable -->
        <profile>
            <id>dev</id>
            <properties>
              <property name="cloud" value="test"/><!--It doesn't work-->
              <server>central</server><!--It works, but it can't be used  ${server}-->
              <example>test</example><!--works fine-->
            </properties>
        </profile>

    </profiles>

  <activeProfiles>
    <activeProfile>dev</activeProfile>
  </activeProfiles>

  <servers>

<!-- to sign with GPG -->
    <server>
      <id>gpg.passphrase</id>
      <passphrase>GPG PASSPHRASE</passphrase>
    </server>

<server>
  <id>central</id>
  <username>USER TOKEN/username>
  <password>PASSWORD GENERATED BY CENTRAL SONATYPE</password>
</server>

  </servers>

</settings>

Maven central generates the configuration to paste

Tap the following buttons

Obviously log in to sonatype

Captura de pantalla 2024-12-23 a la(s) 13 12 34

View account, top right, in the menu that opens on our user

Captura de pantalla 2024-12-23 a la(s) 13 14 46
Captura de pantalla 2024-12-23 a la(s) 13 14 56

Tap ok in the window that opens

Captura de pantalla 2024-12-23 a la(s) 13 15 08

Other details:

Captura de pantalla 2024-12-22 a la(s) 21 48 51
Captura de pantalla 2024-12-22 a la(s) 21 49 05

Swipe on that window that opens.

Captura de pantalla 2024-12-22 a la(s) 21 49 26
<server>
    <id>central</id>
    <username>USER TOKEN GENERATED BY CENTRAL SONATYPE</username>
    <password>PASSWORD GENERATED BY CENTRAL SONATYPE</password>
</server>

Where it says ${server} it goes central, I couldn’t create a property called server, it’s always null.

It’s a reserved word, I think that’s why.

Hardcode the “center”.

Project website

https://leibnix.com

It is https, pay for a web domain, there are free ones, but it will take a long time to find it, and you also have to have control of the DNS part because you have to upload a TXT RECORD, otherwise the domain is not useful because you will not be able to validate the maven namespace.

I did it in

https://latincloud.com/

It is a hosting, it has wordpress to install and I chose a blog type template for wordpress, that is, a cms.

Public server for GPG public keys

The public key can be displayed, the private key cannot.

GPG creates a public/private key pair.

Este es un servidor de claves publicas de GPG:
https://keyserver.ubuntu.com/

What is it for?

There are several things that need to be signed with GPG, but the signature has to be valid, for that the public key has to appear on a public key server.

mine is:
https://keyserver.ubuntu.com/pks/lookup?search=E19C4766EF0B89AFDE176D839C0C50DE9E0722C9&fingerprint=on&op=index

To view your public key

gpg --list-keys

To upload it to a public key server it is not online, it is in the terminal

gpg --send-keys KEY_ID --keyserver hkp://keyserver.ubuntu.com

Replace KEY_ID with the ID of your public key.

Create a namespace in maven and validate it

I hid the verification key in the images, which is then used to verify that the web domain belongs to us. The verification key is placed in a TXT RECORD in the web page’s DNS.
Maven checks your TXT record, the key and if it matches the one they gave you, then it correctly verifies your Maven domain. Mine is com.leibnix

That’s in “Add namespace”

add namespace

Image of unverified namespace.

To go from unverified to pending you have to touch the button on the right, a banner will open and you have to click OK, at this point it took me a while to realize that.

namespace no-verificado

Image of namespace pending verification, verification is all automatic.

namespace verification pending

Verified namespace image.

spacename verificado

Add a code to the web txt record

what is a txt record?

In short, a website can be queried via DNS for its txt record.

Maven reads the txt record and checks that the verification code is there to validate that the website belongs to you.

This is used to validate the maven namespace.

There are 2 commands per terminal to see what the txt record contains:

dig y host

where the verification key appears, which is what the record txt contains, is crossed out

comando dig record txt
comando host

The steps to add the txt record, is an example of what I do in my hosting

www.latincloud.com

In the web panel, go to dns

dns

Then put new dns

Captura de pantalla 2024-12-19 a la(s) 17 35 13

Put in Data the verification code that Maven gives us and in type choose TXT

Captura de pantalla 2024-12-19 a la(s) 15 20 57
Captura de pantalla 2024-12-19 a la(s) 15 21 23

Plugin to upload code to maven

        <plugin>
          <groupId>org.sonatype.central</groupId>
          <artifactId>central-publishing-maven-plugin</artifactId>
          <version>0.6.0</version>
          <extensions>true</extensions>
          <configuration>
            <publishingServerId>central</publishingServerId>
          </configuration>
        </plugin>

This will allow you to upload your artifact to maven central, managed by sonatype

mvn deploy

All together it would be, the verify signature:
You need to make a package first

mvn  clean compile package javadoc:jar verify deploy

You can see its status in central sonatype

mvn deploy ok

and the new dependency generated

dependencia en sonatype

You can also publish artifacts by hand, but I don’t know how it works.

button publicar componente
publicar manualmente2
publicar manualmente3
publicar manualmente4

I didn’t look into it much, I think there are several things that need to be uploaded.

Visualize the dependency

Here you can see your versions
https://repo1.maven.org/maven2/com/leibnix/newlibrary/

Here are your deploys:
https://central.sonatype.com/publishing/deployments

Your namespaces:
https://central.sonatype.com/publishing/namespaces

Example, see the dependency:
https://central.sonatype.com/artifact/com.leibnix/newlibrary
Put your groupId and artifact

After a few days it is indexed here, 4 days, it doesn’t matter if it is not a skillful one:
https://mvnrepository.com/

Example:
https://mvnrepository.com/artifact/com.leibnix/newlibrary

Variables in pom or settings

There are reserved words that cannot be used such as server.

It is used with ${}, example: ${example}

The neatest way is to define them in profiles and activate those profiles:

      <!-- Define a variable -->
        <profile>
            <id>dev</id>
            <properties>
              <property name="cloud" value="test"/><!--It doesn't work-->
              <server>central</server> <!-- the value of ${server} is null -->
              <example>test</example><!-- works fine -->
            </properties>
        </profile>

    </profiles>

  <activeProfiles>
    <activeProfile>dev</activeProfile>
  </activeProfiles>

They can be added in the pom.xml or settings.xml

I added the following plugin to log variables in the terminal:

        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-antrun-plugin</artifactId>
          <version>3.1.0</version>
          <executions>
            <execution>
              <phase>compile</phase>
              <goals>
                <goal>run</goal>
              </goals>
              <configuration>
                <target>
                  <echo message="text to display in terminal and the value of the example property of settings.xml ${example} is displayed"/>
                </target>
              </configuration>
            </execution>
          </executions>
        </plugin>

Example see the example property of the dev profile

mvn help:evaluate -Dexpression=example -Pdev

Show which profiles are active:

mvn help:active-profiles