♻️ Big Refactoring + Support for Snapshots

This commit is contained in:
Andreas Dinauer 2026-04-25 12:25:59 +02:00
parent 53809f294b
commit 4007f3216d
67 changed files with 2112 additions and 693 deletions

View File

@ -1,12 +1,9 @@
package dev.dinauer.maven;
import dev.dinauer.maven.maven.core.Service;
import dev.dinauer.maven.maven.core.release.ReleaseService;
import io.quarkus.arc.profile.IfBuildProfile;
import io.quarkus.runtime.Startup;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import java.io.IOException;
import java.nio.file.Files;
@ -16,16 +13,8 @@ import java.nio.file.Path;
@IfBuildProfile("dev")
public class Dev
{
@Inject
Service service;
@GET
public void init() throws IOException
public void init()
{
service.upload("/org/postgresql/postgresql/41.4.9/postgresql-41.4.9.jar", Files.readAllBytes(Path.of("/home/andreas/Documents/dev/maven/core/src/main/resources/jar/postgresql-42.7.9.jar"))).close();
service.upload("/org/postgresql/postgresql/41.4.9/postgresql-41.4.9.pom", Files.readAllBytes(Path.of("/home/andreas/Documents/dev/maven/core/src/main/resources/jar/postgresql-42.7.9.pom"))).close();
service.upload("/com/fasterxml/jackson/core/jackson-core/2.21.0/jackson-core-2.21.0.jar", Files.readAllBytes(Path.of("/home/andreas/Documents/dev/maven/core/src/main/resources/jar/jackson-core-2.21.0.jar"))).close();
service.upload("/com/fasterxml/jackson/core/jackson-core/2.21.0/jackson-core-2.21.0.pom", Files.readAllBytes(Path.of("/home/andreas/Documents/dev/maven/core/src/main/resources/jar/jackson-core-2.21.0.pom"))).close();
}
}

View File

@ -1,9 +1,9 @@
package dev.dinauer.maven.app;
import dev.dinauer.maven.maven.core.ArtifactRepo;
import dev.dinauer.maven.user.User;
import dev.dinauer.maven.jpa.maven.Version;
import dev.dinauer.maven.jpa.maven.repo.ArtifactRepo;
import dev.dinauer.maven.jpa.maven.ArtifactId;
import dev.dinauer.maven.maven.core.release.ReleaseVersion;
import dev.dinauer.maven.maven.core.ArtifactId;
import dev.dinauer.maven.user.UserClient;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
@ -53,9 +53,9 @@ public class ArtifactResource
Map<String, User> accounts = getAccounts(artifactIds);
for (ArtifactId artifactId : artifactIds)
{
for (Version version : artifactId.getVersions())
for (ReleaseVersion releaseVersion : artifactId.getVersions())
{
version.setUploadAccount(accounts.get(version.getUploadedBy()));
releaseVersion.setUploadAccount(accounts.get(releaseVersion.getUploadedBy()));
}
}
return artifactIds;
@ -66,9 +66,9 @@ public class ArtifactResource
List<String> ids = new ArrayList<>();
for (ArtifactId artifactId : artifactIds)
{
for (Version version : artifactId.getVersions())
for (ReleaseVersion releaseVersion : artifactId.getVersions())
{
ids.add(version.getUploadedBy());
ids.add(releaseVersion.getUploadedBy());
}
}

View File

@ -1,7 +1,7 @@
package dev.dinauer.maven.app;
import dev.dinauer.maven.jpa.maven.repo.GroupRepo;
import dev.dinauer.maven.jpa.maven.GroupId;
import dev.dinauer.maven.maven.core.GroupRepo;
import dev.dinauer.maven.maven.core.GroupId;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.ws.rs.DELETE;

View File

@ -1,17 +0,0 @@
package dev.dinauer.maven.jpa.maven.repo;
import dev.dinauer.maven.jpa.maven.Version;
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
import io.quarkus.panache.common.Parameters;
import jakarta.enterprise.context.ApplicationScoped;
import java.util.Optional;
@ApplicationScoped
public class VersionRepo implements PanacheRepositoryBase<Version, String>
{
public Optional<Version> findOptionalByVersion(String groupId, String artifactId, String version)
{
return find("version = :version AND artifact.artifactId = :artifactId AND artifact.group.groupId = :groupId", Parameters.with("groupId", groupId).and("artifactId", artifactId).and("version", version)).firstResultOptional();
}
}

View File

@ -1,17 +1,16 @@
package dev.dinauer.maven.jpa.maven;
package dev.dinauer.maven.maven.core;
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import dev.dinauer.maven.maven.core.release.ReleaseVersion;
import dev.dinauer.maven.maven.core.snapshot.SnapshotVersion;
import jakarta.persistence.*;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
@Entity
@Table(name = "artifact_id")
@ -34,7 +33,11 @@ public class ArtifactId
@OneToMany(mappedBy = "artifact", cascade = CascadeType.REMOVE)
@JsonManagedReference
private List<Version> versions = new ArrayList<>();
private List<ReleaseVersion> releaseVersions = new ArrayList<>();
@OneToMany(mappedBy = "artifact", cascade = CascadeType.REMOVE)
@JsonManagedReference
private List<SnapshotVersion> snapshotVersions = new ArrayList<>();
@Column(name = "updated_at")
private ZonedDateTime updatedAt;
@ -86,14 +89,14 @@ public class ArtifactId
return this;
}
public List<Version> getVersions()
public List<ReleaseVersion> getVersions()
{
return versions.stream().sorted(Comparator.comparing(version -> new DefaultArtifactVersion(version.getVersion()))).toList().reversed();
return releaseVersions.stream().sorted(Comparator.comparing(version -> new DefaultArtifactVersion(version.getVersion()))).toList().reversed();
}
public ArtifactId setVersions(List<Version> versions)
public ArtifactId setVersions(List<ReleaseVersion> releaseVersions)
{
this.versions = versions;
this.releaseVersions = releaseVersions;
return this;
}
@ -122,10 +125,32 @@ public class ArtifactId
public int getTotalPullCount()
{
int i = 0;
for (Version version : versions)
for (ReleaseVersion releaseVersion : releaseVersions)
{
i = i + version.getPullCount();
i = i + releaseVersion.getPullCount();
}
return i;
}
public List<SnapshotVersion> getSnapshotVersions()
{
return snapshotVersions;
}
public ArtifactId setSnapshotVersions(List<SnapshotVersion> snapshotVersions)
{
this.snapshotVersions = snapshotVersions;
return this;
}
public List<ReleaseVersion> getReleaseVersions()
{
return releaseVersions;
}
public ArtifactId setReleaseVersions(List<ReleaseVersion> releaseVersions)
{
this.releaseVersions = releaseVersions;
return this;
}
}

View File

@ -1,6 +1,5 @@
package dev.dinauer.maven.jpa.maven.repo;
package dev.dinauer.maven.maven.core;
import dev.dinauer.maven.jpa.maven.ArtifactId;
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
import io.quarkus.panache.common.Parameters;
import jakarta.enterprise.context.ApplicationScoped;

View File

@ -1,10 +1,8 @@
package dev.dinauer.maven.maven.core;
import dev.dinauer.maven.jpa.maven.ArtifactId;
import dev.dinauer.maven.jpa.maven.GroupId;
import dev.dinauer.maven.jpa.maven.repo.ArtifactRepo;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.NotFoundException;
import java.time.ZonedDateTime;
import java.util.Optional;
@ -29,7 +27,23 @@ public class ArtifactService
{
GroupId group = groupService.findOrCreate(groupId);
group.setUpdatedAt(ZonedDateTime.now());
return new ArtifactId().setArtifactId(artifactId).setGroup(group).setGroupId(groupId).setCreatedAt(ZonedDateTime.now());
ArtifactId created = new ArtifactId().setArtifactId(artifactId).setGroup(group).setGroupId(groupId).setCreatedAt(ZonedDateTime.now());
artifactRepo.persist(created);
return created;
}
}
public ArtifactId require(String groupId, String artifactId)
{
Optional<ArtifactId> artifactOptional = artifactRepo.findOptionalByArtifactId(groupId, artifactId);
if (artifactOptional.isPresent())
{
return artifactOptional.get();
}
else
{
throw new NotFoundException();
}
}

View File

@ -1,4 +1,4 @@
package dev.dinauer.maven.jpa.maven;
package dev.dinauer.maven.maven.core;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import jakarta.persistence.*;

View File

@ -1,6 +1,5 @@
package dev.dinauer.maven.jpa.maven.repo;
package dev.dinauer.maven.maven.core;
import dev.dinauer.maven.jpa.maven.GroupId;
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
import io.quarkus.panache.common.Parameters;
import jakarta.enterprise.context.ApplicationScoped;
@ -14,4 +13,4 @@ public class GroupRepo implements PanacheRepositoryBase<GroupId, String>
{
return find("groupId = :groupId", Parameters.with("groupId", groupId)).firstResultOptional();
}
}
}

View File

@ -1,7 +1,5 @@
package dev.dinauer.maven.maven.core;
import dev.dinauer.maven.jpa.maven.GroupId;
import dev.dinauer.maven.jpa.maven.repo.GroupRepo;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

View File

@ -1,72 +0,0 @@
package dev.dinauer.maven.maven.core;
import dev.dinauer.maven.jpa.maven.Jar;
import dev.dinauer.maven.jpa.maven.Version;
import dev.dinauer.maven.maven.core.model.MavenContext;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.apache.commons.codec.digest.DigestUtils;
import java.util.List;
@ApplicationScoped
public class JarService
{
@Inject
VersionService versionService;
public Response store(MavenContext mavenContext, byte[] body)
{
String md5 = DigestUtils.md5Hex(body);
String sha1 = DigestUtils.sha1Hex(body);
Version version = versionService.findOrCreate(mavenContext.groupId(), mavenContext.artifactId(), mavenContext.version().getRaw());
if (!existsJar(version.getJars(), mavenContext.file().getBase()))
{
version.getJars().add(new Jar().setJar(body).setMd5(md5).setSha1(sha1).setVersion(version).setUrl(mavenContext.path()).setFilename(mavenContext.file().getFilename()));
versionService.persist(version);
return Response.status(Response.Status.CREATED).build();
}
return Response.status(Response.Status.CONFLICT).build();
}
public Response find(MavenContext context)
{
return Response.status(Response.Status.OK).type(MediaType.APPLICATION_OCTET_STREAM).entity(findJar(context).getJar()).build();
}
public Response findHash(MavenContext context)
{
switch (context.file().getHash())
{
case SHA1 ->
{
return Response.status(Response.Status.OK).type(MediaType.TEXT_PLAIN).entity(findJar(context).getSha1()).build();
}
case MD5 ->
{
return Response.status(Response.Status.OK).type(MediaType.TEXT_PLAIN).entity(findJar(context).getMd5()).build();
}
}
throw new BadRequestException();
}
private Jar findJar(MavenContext context)
{
return versionService.findOptional(context.groupId(), context.artifactId(), context.version().getRaw()).orElseThrow().getJarByFilename(context.file().getFilename()).orElseThrow();
}
private boolean existsJar(List<Jar> jars, String fileBase)
{
for (Jar jar : jars)
{
if (fileBase.equals(jar.getFilename()))
{
return true;
}
}
return false;
}
}

View File

@ -1,32 +1,47 @@
package dev.dinauer.maven.maven.core;
import dev.dinauer.maven.maven.core.context.ArtifactContext;
import dev.dinauer.maven.maven.core.context.VersionContext;
import dev.dinauer.maven.maven.core.model.*;
import dev.dinauer.maven.maven.core.parser.MavenMetadataUrlParser;
import dev.dinauer.maven.maven.core.parser.MavenUrlParser;
import org.apache.commons.io.FilenameUtils;
import dev.dinauer.maven.maven.shared.Extensions;
import dev.dinauer.maven.maven.shared.ParserResult;
import java.nio.file.Path;
public class MavenContextProvider
{
public static MavenContext parse(String path)
public static ArtifactContext parse(String path)
{
ExtensionParser.Result result = ExtensionParser.parse(Path.of(path).getFileName().toString());
switch (result.ext())
ParserResult<Extensions> extensionResult = new ExtensionParser().parse(Path.of(path).getFileName().toString());
switch (extensionResult.result().ext())
{
case JAR, POM ->
{
MavenUrlParser parser = MavenUrlParser.parse(path);
String artifact = parser.artifactId();
Version version = parser.version();
return new MavenContext(path, parser.groupId(), artifact, version, FileParser.parse(artifact, version.getRaw(), Path.of(path).getFileName().toString()));
return new VersionContext(extensionResult.result(), parser.groupId(), parser.artifactId(), extensionResult.remainder(), parser.version());
}
case XML ->
{
MavenMetadataUrlParser parser = MavenMetadataUrlParser.parse(path);
return new MavenContext(path, parser.groupId(), parser.artifactId(), null, new File().setBase("maven-metadata").setType(result.ext()).setHash(result.hashExt()));
if (isSnapshot(path))
{
MavenUrlParser parser = MavenUrlParser.parse(path);
return new VersionContext(extensionResult.result(), parser.groupId(), parser.artifactId(), extensionResult.remainder(), parser.version());
}
else
{
MavenMetadataUrlParser parser = MavenMetadataUrlParser.parse(path);
return new ArtifactContext(extensionResult.result(), parser.groupId(), parser.artifactId(), extensionResult.remainder());
}
}
}
throw new RuntimeException();
}
private static boolean isSnapshot(String path)
{
String[] sections = path.split("/");
return sections[sections.length - 2].endsWith("-SNAPSHOT");
}
}

View File

@ -0,0 +1,113 @@
package dev.dinauer.maven.maven.core;
import com.fasterxml.jackson.core.JsonProcessingException;
import dev.dinauer.maven.maven.core.context.ArtifactContext;
import dev.dinauer.maven.maven.core.context.ReleaseContext;
import dev.dinauer.maven.maven.core.context.SnapshotContext;
import dev.dinauer.maven.maven.core.context.VersionContext;
import dev.dinauer.maven.maven.core.model.*;
import dev.dinauer.maven.maven.core.model.Version;
import dev.dinauer.maven.maven.core.release.ReleaseService;
import dev.dinauer.maven.maven.core.release.parser.ReleaseFile;
import dev.dinauer.maven.maven.core.release.parser.ReleaseFileParser;
import dev.dinauer.maven.maven.core.snapshot.SnapshotFile;
import dev.dinauer.maven.maven.core.snapshot.SnapshotPom;
import dev.dinauer.maven.maven.core.snapshot.SnapshotService;
import dev.dinauer.maven.maven.core.snapshot.parser.SnapshotFileParser;
import dev.dinauer.maven.maven.shared.Extensions;
import dev.dinauer.maven.maven.shared.MavenCoordinates;
import dev.dinauer.maven.maven.shared.ParserResult;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.apache.commons.lang3.NotImplementedException;
import java.util.List;
import java.util.Objects;
@ApplicationScoped
public class MavenService
{
@Inject
ReleaseService releaseService;
@Inject
SnapshotService snapshotService;
public void upload(String path, byte[] body)
{
ArtifactContext context = MavenContextProvider.parse(path);
if (context.getClass() == VersionContext.class)
{
if (List.of(FileExt.JAR, FileExt.POM).contains(context.extensions().ext()) && context.extensions().hashExt().isEmpty())
{
VersionContext versionContext = (VersionContext) context;
if (versionContext.isSnapshot())
{
snapshotService.upload(toSnapshotContext(versionContext), body);
}
else
{
releaseService.upload(toReleaseContext(versionContext), body);
}
}
if (Objects.equals(FileExt.XML, context.extensions().ext()))
{
throw new NotImplementedException(); // Hand over to XML metadata service
}
}
if (context.getClass() == ArtifactContext.class)
{
throw new NotImplementedException(); // Hand over to XML Snapshot artifact metadata service
}
}
public Response serve(String path)
{
ArtifactContext context = MavenContextProvider.parse(path);
if (context.getClass() == VersionContext.class)
{
VersionContext versionContext = (VersionContext) context;
if (versionContext.isSnapshot())
{
return snapshotService.serve(toSnapshotContext(versionContext));
}
else
{
return releaseService.serve(toReleaseContext(versionContext));
}
}
throw new BadRequestException();
}
private SnapshotContext toSnapshotContext(VersionContext versionContext)
{
ParserResult<SnapshotFile> result = new SnapshotFileParser(versionContext.artifactId(), versionContext.base()).parse(versionContext.filename());
return new SnapshotContext(
versionContext.extensions(),
versionContext.groupId(),
versionContext.artifactId(),
versionContext.filename(),
versionContext.version(),
result.result().getDate(),
result.result().getTime(),
result.result().getBuildNumber(),
result.result().getClassifier()
);
}
private ReleaseContext toReleaseContext(VersionContext versionContext)
{
ParserResult<ReleaseFile> result = new ReleaseFileParser(versionContext.artifactId(), versionContext.base()).parse(versionContext.filename());
return new ReleaseContext(
versionContext.extensions(),
versionContext.groupId(),
versionContext.artifactId(),
versionContext.filename(),
versionContext.version(),
result.result().getClassifier()
);
}
}

View File

@ -1,65 +0,0 @@
package dev.dinauer.maven.maven.core;
import dev.dinauer.maven.jpa.maven.Pom;
import dev.dinauer.maven.jpa.maven.Version;
import dev.dinauer.maven.maven.core.model.MavenContext;
import dev.dinauer.maven.maven.token.TokenService;
import io.quarkus.security.identity.SecurityIdentity;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.apache.commons.codec.digest.DigestUtils;
import java.time.ZonedDateTime;
@ApplicationScoped
public class PomService
{
@Inject
SecurityIdentity securityIdentity;
@Inject
VersionService versionService;
public Response store(MavenContext mavenContext, byte[] body)
{
String md5 = DigestUtils.md5Hex(body);
String sha1 = DigestUtils.sha1Hex(body);
Version version = versionService.findOrCreate(mavenContext.groupId(), mavenContext.artifactId(), mavenContext.version().getRaw());
version.setUploadedBy(securityIdentity.getPrincipal().getName());
if (version.getPom() == null)
{
version.setPom(new Pom().setPom(new String(body)).setMd5(md5).setSha1(sha1).setVersion(version).setUrl(mavenContext.path()).setFilename(mavenContext.file().getFilename()));
versionService.persist(version);
return Response.status(Response.Status.CREATED).build();
}
return Response.status(Response.Status.CONFLICT).build();
}
public Response findHash(MavenContext context)
{
Version version = versionService.findOptional(context.groupId(), context.artifactId(), context.version().getRaw()).orElseThrow();
switch (context.file().getHash())
{
case SHA1 ->
{
return Response.status(Response.Status.OK).type(MediaType.TEXT_PLAIN).entity(version.getPom().getSha1()).build();
}
case MD5 ->
{
return Response.status(Response.Status.OK).type(MediaType.TEXT_PLAIN).entity(version.getPom().getMd5()).build();
}
}
throw new BadRequestException();
}
public Response find(MavenContext mavenContext)
{
Version version = versionService.findOptional(mavenContext.groupId(), mavenContext.artifactId(), mavenContext.version().getRaw()).orElseThrow();
version.setLastPulled(ZonedDateTime.now());
version.incrementPullCount();
return Response.status(Response.Status.OK).type(MediaType.APPLICATION_XML).entity(version.getPom().getPom()).build();
}
}

View File

@ -1,130 +0,0 @@
package dev.dinauer.maven.maven.core;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import dev.dinauer.maven.jpa.maven.ArtifactId;
import dev.dinauer.maven.jpa.maven.Jar;
import dev.dinauer.maven.jpa.maven.Pom;
import dev.dinauer.maven.jpa.maven.Version;
import dev.dinauer.maven.maven.core.model.ExtensionParser;
import dev.dinauer.maven.maven.core.model.File;
import dev.dinauer.maven.maven.token.TokenService;
import dev.dinauer.maven.metadata.Metadata;
import dev.dinauer.maven.maven.core.model.FileHash;
import dev.dinauer.maven.maven.core.model.MavenContext;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jdk.jshell.spi.ExecutionControl;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.NotImplementedException;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Optional;
@ApplicationScoped
public class Service
{
@Inject
ArtifactService artifactService;
@Inject
JarService jarService;
@Inject
PomService pomService;
@Transactional
public Response upload(String path, byte[] body)
{
MavenContext mavenContext = MavenContextProvider.parse(path);
switch (mavenContext.file().getType())
{
case JAR ->
{
if (mavenContext.version().getSnapshot())
{
throw new WebApplicationException(501);
}
if (FileHash.NONE.equals(mavenContext.file().getHash()))
{
return jarService.store(mavenContext, body);
}
return Response.accepted().build();
}
case POM ->
{
if (mavenContext.version().getSnapshot())
{
throw new WebApplicationException(501);
}
if (FileHash.NONE.equals(mavenContext.file().getHash()))
{
return pomService.store(mavenContext, body);
}
return Response.accepted().build();
}
case XML ->
{
return Response.accepted().build();
}
}
throw new RuntimeException();
}
@Transactional
public Response serve(String path) throws JsonProcessingException
{
MavenContext mavenContext = MavenContextProvider.parse(path);
switch (mavenContext.file().getType())
{
case JAR ->
{
if (FileHash.NONE.equals(mavenContext.file().getHash()))
{
return jarService.find(mavenContext);
}
return jarService.findHash(mavenContext);
}
case POM ->
{
if (FileHash.NONE.equals(mavenContext.file().getHash()))
{
return pomService.find(mavenContext);
}
return pomService.findHash(mavenContext);
}
case XML ->
{
Optional<ArtifactId> artifactOptional = artifactService.find(mavenContext.groupId(), mavenContext.artifactId());
if (artifactOptional.isPresent())
{
ArtifactId artifact = artifactOptional.get();
String metadata = new XmlMapper().writeValueAsString(new Metadata(artifact.getGroupId(), artifact.getArtifactId(), null, artifact.getUpdatedAt().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))));
if (mavenContext.file().getHash() == null)
{
return Response.ok().type(MediaType.APPLICATION_XML).entity(metadata).build();
}
switch (mavenContext.file().getHash())
{
case SHA1 ->
{
return Response.ok().type(MediaType.TEXT_PLAIN).entity(DigestUtils.sha1Hex(metadata)).build();
}
case MD5 ->
{
return Response.ok().type(MediaType.TEXT_PLAIN).entity(DigestUtils.md5Hex(metadata)).build();
}
}
}
return Response.status(Response.Status.NOT_FOUND).build();
}
}
throw new RuntimeException();
}
}

View File

@ -0,0 +1,6 @@
package dev.dinauer.maven.maven.core;
public interface Version
{
boolean isSnapshot();
}

View File

@ -1,58 +0,0 @@
package dev.dinauer.maven.maven.core;
import dev.dinauer.maven.event.Event;
import dev.dinauer.maven.event.EventType;
import dev.dinauer.maven.event.Resource;
import dev.dinauer.maven.event.repo.EventRepo;
import dev.dinauer.maven.jpa.maven.ArtifactId;
import dev.dinauer.maven.jpa.maven.Version;
import dev.dinauer.maven.jpa.maven.repo.VersionRepo;
import dev.dinauer.maven.maven.token.TokenService;
import io.quarkus.security.identity.SecurityIdentity;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.time.ZonedDateTime;
import java.util.Optional;
@ApplicationScoped
public class VersionService
{
@Inject
VersionRepo versionRepo;
@Inject
ArtifactService artifactService;
@Inject
EventRepo eventRepo;
@Inject
SecurityIdentity securityIdentity;
public Optional<Version> findOptional(String groupId, String artifactId, String version)
{
return versionRepo.findOptionalByVersion(groupId, artifactId, version);
}
public Version findOrCreate(String groupId, String artifactId, String version)
{
Optional<Version> versionOptional = versionRepo.findOptionalByVersion(groupId, artifactId, version);
if (versionOptional.isPresent())
{
return versionOptional.get();
}
else
{
ArtifactId artifact = artifactService.findOrCreate(groupId, artifactId);
artifact.setUpdatedAt(ZonedDateTime.now());
return new Version().setVersion(version).setArtifact(artifact).setGroupId(groupId).setArtifactId(artifactId);
}
}
public void persist(Version version)
{
versionRepo.persist(version);
eventRepo.persist(new Event().setAccountId(securityIdentity.getPrincipal().getName()).setType(EventType.UPLOAD).setResource(new Resource().setGroupId(version.getGroupId()).setArtifactId(version.getArtifactId()).setVersion(version.getVersion())).setTimestamp(ZonedDateTime.now()));
}
}

View File

@ -0,0 +1,39 @@
package dev.dinauer.maven.maven.core.context;
import dev.dinauer.maven.maven.shared.Extensions;
public class ArtifactContext
{
private final Extensions extensions;
private final String groupId;
private final String artifactId;
private final String filename;
public ArtifactContext(Extensions extensions, String groupId, String artifactId, String filename)
{
this.extensions = extensions;
this.groupId = groupId;
this.artifactId = artifactId;
this.filename = filename;
}
public Extensions extensions()
{
return extensions;
}
public String groupId()
{
return groupId;
}
public String artifactId()
{
return artifactId;
}
public String filename()
{
return filename;
}
}

View File

@ -0,0 +1,21 @@
package dev.dinauer.maven.maven.core.context;
import dev.dinauer.maven.maven.shared.Extensions;
import java.util.Optional;
public class ReleaseContext extends VersionContext
{
private final String classifier;
public ReleaseContext(Extensions extensions, String groupId, String artifactId, String filename, String version, String classifier)
{
super(extensions, groupId, artifactId, filename, version);
this.classifier = classifier;
}
public Optional<String> classifier()
{
return Optional.ofNullable(classifier);
}
}

View File

@ -0,0 +1,42 @@
package dev.dinauer.maven.maven.core.context;
import dev.dinauer.maven.maven.shared.Extensions;
import java.util.Optional;
public class SnapshotContext extends VersionContext
{
private final String date;
private final String time;
private final int buildNumber;
private final String classifier;
public SnapshotContext(Extensions extensions, String groupId, String artifactId, String filename, String version, String date, String time, int buildNumber, String classifier)
{
super(extensions, groupId, artifactId, filename, version);
this.date = date;
this.time = time;
this.buildNumber = buildNumber;
this.classifier = classifier;
}
public String date()
{
return date;
}
public String time()
{
return time;
}
public int buildNumber()
{
return buildNumber;
}
public Optional<String> classifier()
{
return Optional.ofNullable(classifier);
}
}

View File

@ -0,0 +1,32 @@
package dev.dinauer.maven.maven.core.context;
import dev.dinauer.maven.maven.shared.Extensions;
import org.apache.commons.lang3.Strings;
public class VersionContext extends ArtifactContext
{
private static final String SNAPSHOT_SUFFIX = "-SNAPSHOT";
private final String version;
public VersionContext(Extensions extensions, String groupId, String artifactId, String filename, String version)
{
super(extensions, groupId, artifactId, filename);
this.version = version;
}
public String version()
{
return version;
}
public boolean isSnapshot()
{
return Strings.CI.endsWith(version, SNAPSHOT_SUFFIX);
}
public String base()
{
return Strings.CI.removeEnd(version, SNAPSHOT_SUFFIX);
}
}

View File

@ -1,22 +1,26 @@
package dev.dinauer.maven.maven.core.model;
import dev.dinauer.maven.maven.shared.Extensions;
import dev.dinauer.maven.maven.shared.Parser;
import dev.dinauer.maven.maven.shared.ParserResult;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.Strings;
import java.util.List;
import java.util.Optional;
public class ExtensionParser
public class ExtensionParser implements Parser<Extensions>
{
private static final List<String> FILE_TYPES = List.of("jar", "pom", "xml");
private static final List<String> FILE_HASHES = List.of("md5", "sha1", "sha256", "sha512");
public static Result parse(String raw)
public ParserResult<Extensions> parse(String raw)
{
String firstLevelExtension = FilenameUtils.getExtension(raw);
String firstLevelBase = Strings.CI.removeEnd(raw, String.format(".%s", firstLevelExtension));
if (FILE_TYPES.contains(firstLevelExtension))
{
return new Result(firstLevelBase, FileExt.valueOf(firstLevelExtension.toUpperCase()), null);
return new ParserResult<>(firstLevelBase, new Extensions(FileExt.valueOf(firstLevelExtension.toUpperCase()), Optional.empty()));
}
if (FILE_HASHES.contains(firstLevelExtension))
{
@ -24,13 +28,9 @@ public class ExtensionParser
if (FILE_TYPES.contains(secondLevelExtension))
{
String secondLevelBase = Strings.CI.removeEnd(firstLevelBase, String.format(".%s", secondLevelExtension));
return new Result(secondLevelBase, FileExt.valueOf(secondLevelExtension.toUpperCase()), FileHash.valueOf(firstLevelExtension.toUpperCase()));
return new ParserResult<>(secondLevelBase, new Extensions(FileExt.valueOf(secondLevelExtension.toUpperCase()), Optional.of(FileHash.valueOf(firstLevelExtension.toUpperCase()))));
}
}
throw new RuntimeException();
}
public record Result(String base, FileExt ext, FileHash hashExt)
{
}
}

View File

@ -1,5 +1,78 @@
package dev.dinauer.maven.maven.core.model;
public record MavenContext(String path, String groupId, String artifactId, Version version, File file)
import dev.dinauer.maven.maven.shared.Extensions;
public class MavenContext
{
}
private String path;
private String groupId;
private String artifactId;
private Version version;
private String filename;
private Extensions extensions;
public MavenContext(String path, String groupId, String artifactId, Version version, String filename, Extensions extensions)
{
this.path = path;
this.groupId = groupId;
this.artifactId = artifactId;
this.version = version;
this.filename = filename;
this.extensions = extensions;
}
public String path()
{
return path;
}
public void setPath(String path)
{
this.path = path;
}
public String groupId()
{
return groupId;
}
public void setGroupId(String groupId)
{
this.groupId = groupId;
}
public String artifactId()
{
return artifactId;
}
public void setArtifactId(String artifactId)
{
this.artifactId = artifactId;
}
public Version version()
{
return version;
}
public void setVersion(Version version)
{
this.version = version;
}
public String filename()
{
return filename;
}
public void setFilename(String filename)
{
this.filename = filename;
}
public Extensions extensions()
{
return extensions;
}
}

View File

@ -1,80 +0,0 @@
package dev.dinauer.maven.maven.core.model;
import jakarta.ws.rs.BadRequestException;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.Strings;
import java.util.List;
import java.util.OptionalInt;
import java.util.stream.IntStream;
public class SnapshotFileParser
{
public static SnapshotFile parse(String artifactId, String version, String raw)
{
ExtensionParser.Result extensionResult = ExtensionParser.parse(raw);
SnapshotFile file = new SnapshotFile();
file.setBase(extensionResult.base());
file.setType(extensionResult.ext());
file.setHash(extensionResult.hashExt());
file.setRaw(raw);
file.setArtifactId(artifactId);
file.setVersion(version);
String dataString = prepareForData(artifactId, version, raw);
file.setDate(getDate(dataString));
String timeString = prepareForTime(dataString);
file.setTime(getTime(timeString));
String buildNumberString = prepareForBuildNumber(timeString);
file.setBuildNumber(getBuildNumber(buildNumberString));
return file;
}
private static String prepareForData(String artifact, String version, String raw)
{
return Strings.CI.removeStart(raw, String.format("%s-%s-", artifact, prepareVersion(version)));
}
private static String prepareForTime(String path)
{
return Strings.CI.removeStart(path.substring(9), ".");
}
private static String prepareForBuildNumber(String path)
{
return Strings.CI.removeStart(path.substring(7), "-");
}
private static int getBuildNumber(String path)
{
int nextDot = path.indexOf(".");
int nextDash = path.indexOf("-");
OptionalInt optInt = IntStream.of(nextDash, nextDot).filter(index -> index > 0).min();
if (optInt.isPresent())
{
return Integer.parseInt(path.substring(0, optInt.getAsInt()));
}
throw new IllegalArgumentException();
}
private static String getDate(String path)
{
return path.substring(0, 8);
}
private static String getTime(String path)
{
return path.substring(0, 6);
}
private static String prepareVersion(String version)
{
return Strings.CI.removeEnd(version, "-SNAPSHOT");
}
}

View File

@ -1,29 +1,40 @@
package dev.dinauer.maven.maven.core.model;
import org.apache.commons.lang3.Strings;
public class Version
{
private String raw;
private Boolean isSnapshot;
private final String raw;
private final String base;
private final Boolean isSnapshot;
public Version(String raw)
{
this.raw = raw;
if (Strings.CI.endsWith(raw, "-SNAPSHOT"))
{
this.base = Strings.CI.removeEnd(raw, "-SNAPSHOT");
this.isSnapshot = true;
}
else
{
this.base = raw;
this.isSnapshot = false;
}
}
public String getRaw()
{
return raw;
}
public Version setRaw(String raw)
public String getBase()
{
this.raw = raw;
return this;
return base;
}
public Boolean getSnapshot()
public Boolean isSnapshot()
{
return isSnapshot;
}
public Version setSnapshot(Boolean snapshot)
{
isSnapshot = snapshot;
return this;
}
}

View File

@ -1,6 +1,7 @@
package dev.dinauer.maven.maven.core.parser;
import dev.dinauer.maven.maven.core.model.Version;
import dev.dinauer.maven.maven.core.release.ReleaseVersion;
import jakarta.ws.rs.BadRequestException;
import java.util.List;
@ -32,11 +33,15 @@ public class MavenUrlParser
return sections.get(sections.size() - 3);
}
public Version version()
public String version()
{
List<String> sections = getSections(path);
String raw = sections.get(sections.size() - 2);
return new Version().setRaw(raw).setSnapshot(raw.endsWith("-SNAPSHOT"));
return sections.get(sections.size() - 2);
}
public String filename()
{
return getSections(path).getLast();
}
private static List<String> getSections(String path)

View File

@ -1,12 +1,12 @@
package dev.dinauer.maven.jpa.maven;
package dev.dinauer.maven.maven.core.release;
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.persistence.*;
@Entity
@Table(name = "jar")
public class Jar
@Table(name = "release_jar")
public class ReleaseJar
{
@Id
@GeneratedValue(strategy = GenerationType.UUID)
@ -25,17 +25,19 @@ public class Jar
private String filename;
@ManyToOne
@JoinColumn(name = "version_id")
private String classifier;
@ManyToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name = "release_version_id")
@JsonBackReference
private Version version;
private ReleaseVersion releaseVersion;
public String getId()
{
return id;
}
public Jar setId(String id)
public ReleaseJar setId(String id)
{
this.id = id;
return this;
@ -46,7 +48,7 @@ public class Jar
return jar;
}
public Jar setJar(byte[] jar)
public ReleaseJar setJar(byte[] jar)
{
this.jar = jar;
return this;
@ -57,7 +59,7 @@ public class Jar
return md5;
}
public Jar setMd5(String md5)
public ReleaseJar setMd5(String md5)
{
this.md5 = md5;
return this;
@ -68,20 +70,20 @@ public class Jar
return sha1;
}
public Jar setSha1(String sha1)
public ReleaseJar setSha1(String sha1)
{
this.sha1 = sha1;
return this;
}
public Version getVersion()
public ReleaseVersion getReleaseVersion()
{
return version;
return releaseVersion;
}
public Jar setVersion(Version version)
public ReleaseJar setReleaseVersion(ReleaseVersion releaseVersion)
{
this.version = version;
this.releaseVersion = releaseVersion;
return this;
}
@ -90,7 +92,7 @@ public class Jar
return url;
}
public Jar setUrl(String url)
public ReleaseJar setUrl(String url)
{
this.url = url;
return this;
@ -101,9 +103,20 @@ public class Jar
return filename;
}
public Jar setFilename(String filename)
public ReleaseJar setFilename(String filename)
{
this.filename = filename;
return this;
}
public String getClassifier()
{
return classifier;
}
public ReleaseJar setClassifier(String classifier)
{
this.classifier = classifier;
return this;
}
}

View File

@ -0,0 +1,61 @@
package dev.dinauer.maven.maven.core.release;
import dev.dinauer.maven.maven.core.ArtifactService;
import dev.dinauer.maven.maven.core.context.ReleaseContext;
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.NotFoundException;
import java.util.Map;
@ApplicationScoped
public class ReleaseJarRepo implements PanacheRepositoryBase<ReleaseJar, String>
{
@Inject
ReleaseVersionRepo releaseVersionRepo;
public ReleaseJar find(ReleaseContext releaseContext)
{
ReleaseVersion version = releaseVersionRepo.findByGroupIdAndArtifactIdAndVersion(releaseContext.groupId(), releaseContext.artifactId(), releaseContext.version());
if (version != null)
{
if (releaseContext.classifier().isPresent())
{
return find("releaseVersion = :version AND classifier = :classifier", Map.ofEntries(Map.entry("version", version), Map.entry("classifier", releaseContext.classifier().get()))).firstResult();
}
else
{
return find("releaseVersion = :version AND classifier IS NULL", Map.ofEntries(Map.entry("version", version))).firstResult();
}
}
return null;
}
public ReleaseJar require(ReleaseContext releaseContext)
{
ReleaseVersion version = releaseVersionRepo.requireByGroupIdAndArtifactIdAndVersion(releaseContext.groupId(), releaseContext.artifactId(), releaseContext.version());
if (version != null)
{
if (releaseContext.classifier().isPresent())
{
ReleaseJar releaseJar = find("releaseVersion = :version AND classifier = :classifier", Map.ofEntries(Map.entry("version", version), Map.entry("classifier", releaseContext.classifier().get()))).firstResult();
if (releaseJar != null)
{
return releaseJar;
}
throw new NotFoundException();
}
else
{
ReleaseJar releaseJar = find("releaseVersion = :version AND classifier IS NULL", Map.ofEntries(Map.entry("version", version))).firstResult();
if (releaseJar != null)
{
return releaseJar;
}
throw new NotFoundException();
}
}
return null;
}
}

View File

@ -0,0 +1,73 @@
package dev.dinauer.maven.maven.core.release;
import dev.dinauer.maven.maven.core.context.ReleaseContext;
import dev.dinauer.maven.maven.core.context.SnapshotContext;
import dev.dinauer.maven.maven.core.context.VersionContext;
import dev.dinauer.maven.maven.core.model.MavenContext;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.NotImplementedException;
import java.util.List;
@ApplicationScoped
public class ReleaseJarService
{
@Inject
ReleaseJarRepo releaseJarRepo;
@Inject
ReleaseVersionService releaseVersionService;
public void store(ReleaseContext releaseContext, byte[] body)
{
ReleaseJar jar = releaseJarRepo.find(releaseContext);
if (jar == null)
{
create(releaseContext, body);
}
else
{
throw new WebApplicationException(409);
}
}
public byte[] find(ReleaseContext releaseContext)
{
return releaseJarRepo.require(releaseContext).getJar();
}
public String findHash(ReleaseContext releaseContext)
{
switch (releaseContext.extensions().hashExt().orElseThrow())
{
case SHA1 ->
{
return releaseJarRepo.require(releaseContext).getSha1();
}
case MD5 ->
{
return releaseJarRepo.require(releaseContext).getMd5();
}
}
throw new BadRequestException();
}
private void create(ReleaseContext releaseContext, byte[] content)
{
ReleaseJar releaseJar = new ReleaseJar();
releaseJar.setReleaseVersion(releaseVersionService.findOrCreate(releaseContext));
releaseJar.setJar(content);
releaseJar.setMd5(DigestUtils.md5Hex(content));
releaseJar.setSha1(DigestUtils.sha1Hex(content));
releaseJar.setClassifier(releaseContext.classifier().orElse(null));
releaseJar.setFilename(releaseContext.filename());
releaseJarRepo.persist(releaseJar);
}
}

View File

@ -1,12 +1,12 @@
package dev.dinauer.maven.jpa.maven;
package dev.dinauer.maven.maven.core.release;
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.persistence.*;
@Entity
@Table(name = "pom")
public class Pom
@Table(name = "release_pom")
public class ReleasePom
{
@Id
@GeneratedValue(strategy = GenerationType.UUID)
@ -26,16 +26,16 @@ public class Pom
private String filename;
@OneToOne
@JoinColumn(name = "version_id")
@JoinColumn(name = "release_version_id")
@JsonBackReference
private Version version;
private ReleaseVersion releaseVersion;
public String getId()
{
return id;
}
public Pom setId(String id)
public ReleasePom setId(String id)
{
this.id = id;
return this;
@ -46,7 +46,7 @@ public class Pom
return pom;
}
public Pom setPom(String pom)
public ReleasePom setPom(String pom)
{
this.pom = pom;
return this;
@ -57,7 +57,7 @@ public class Pom
return md5;
}
public Pom setMd5(String md5)
public ReleasePom setMd5(String md5)
{
this.md5 = md5;
return this;
@ -68,20 +68,20 @@ public class Pom
return sha1;
}
public Pom setSha1(String sha1)
public ReleasePom setSha1(String sha1)
{
this.sha1 = sha1;
return this;
}
public Version getVersion()
public ReleaseVersion getReleaseVersion()
{
return version;
return releaseVersion;
}
public Pom setVersion(Version version)
public ReleasePom setReleaseVersion(ReleaseVersion releaseVersion)
{
this.version = version;
this.releaseVersion = releaseVersion;
return this;
}
@ -90,7 +90,7 @@ public class Pom
return url;
}
public Pom setUrl(String url)
public ReleasePom setUrl(String url)
{
this.url = url;
return this;
@ -101,7 +101,7 @@ public class Pom
return filename;
}
public Pom setFilename(String filename)
public ReleasePom setFilename(String filename)
{
this.filename = filename;
return this;

View File

@ -0,0 +1,41 @@
package dev.dinauer.maven.maven.core.release;
import dev.dinauer.maven.maven.core.context.ReleaseContext;
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.NotFoundException;
import org.apache.commons.lang3.NotImplementedException;
import java.util.Map;
@ApplicationScoped
public class ReleasePomRepo implements PanacheRepositoryBase<ReleasePom, String>
{
@Inject
ReleaseVersionRepo releaseVersionRepo;
public ReleasePom find(ReleaseContext releaseContext)
{
ReleaseVersion version = releaseVersionRepo.findByGroupIdAndArtifactIdAndVersion(releaseContext.groupId(), releaseContext.artifactId(), releaseContext.version());
if (version != null)
{
return find("releaseVersion = :version", Map.ofEntries(Map.entry("version", version))).firstResult();
}
return null;
}
public ReleasePom require(ReleaseContext releaseContext)
{
ReleaseVersion version = releaseVersionRepo.requireByGroupIdAndArtifactIdAndVersion(releaseContext.groupId(), releaseContext.artifactId(), releaseContext.version());
ReleasePom releasePom = find("releaseVersion = :version", Map.ofEntries(Map.entry("version", version))).firstResult();
if (releasePom != null)
{
return releasePom;
}
else
{
throw new NotFoundException();
}
}
}

View File

@ -0,0 +1,62 @@
package dev.dinauer.maven.maven.core.release;
import dev.dinauer.maven.maven.core.context.ReleaseContext;
import dev.dinauer.maven.maven.core.context.SnapshotContext;
import dev.dinauer.maven.maven.core.model.FileHash;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.WebApplicationException;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.NotImplementedException;
import java.nio.charset.StandardCharsets;
@ApplicationScoped
public class ReleasePomService
{
@Inject
ReleasePomRepo releasePomRepo;
@Inject
ReleaseVersionService releaseVersionService;
public void store(ReleaseContext releaseContext, byte[] body)
{
ReleasePom jar = releasePomRepo.find(releaseContext);
if (jar == null)
{
create(releaseContext, body);
}
else
{
throw new WebApplicationException(409);
}
}
public String find(ReleaseContext releaseContext)
{
return releasePomRepo.require(releaseContext).getPom();
}
public String findHash(ReleaseContext releaseContext)
{
ReleasePom releasePom = releasePomRepo.require(releaseContext);
return switch (releaseContext.extensions().hashExt().orElseThrow())
{
case MD5 -> releasePom.getMd5();
case SHA1 -> releasePom.getSha1();
default -> throw new IllegalStateException("Unexpected value: " + releaseContext.extensions().hashExt());
};
}
private void create(ReleaseContext releaseContext, byte[] content)
{
ReleasePom releasePom = new ReleasePom();
releasePom.setReleaseVersion(releaseVersionService.findOrCreate(releaseContext));
releasePom.setPom(new String(content, StandardCharsets.UTF_8));
releasePom.setMd5(DigestUtils.md5Hex(content));
releasePom.setSha1(DigestUtils.sha1Hex(content));
releasePom.setFilename(releaseContext.filename());
releasePomRepo.persist(releasePom);
}
}

View File

@ -0,0 +1,59 @@
package dev.dinauer.maven.maven.core.release;
import dev.dinauer.maven.maven.core.*;
import dev.dinauer.maven.maven.core.context.ReleaseContext;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.apache.commons.lang3.NotImplementedException;
@ApplicationScoped
public class ReleaseService
{
@Inject
ReleaseJarService releaseJarService;
@Inject
ReleasePomService releasePomService;
@Transactional
public void upload(ReleaseContext releaseContext, byte[] body)
{
switch (releaseContext.extensions().ext())
{
case JAR ->
{
if (releaseContext.extensions().hashExt().isEmpty())
{
releaseJarService.store(releaseContext, body);
}
}
case POM ->
{
if (releaseContext.extensions().hashExt().isEmpty())
{
releasePomService.store(releaseContext, body);
}
}
}
}
public Response serve(ReleaseContext releaseContext)
{
switch (releaseContext.extensions().ext())
{
case JAR ->
{
return Response.status(200).type(MediaType.APPLICATION_OCTET_STREAM).entity(releaseJarService.find(releaseContext)).build();
}
case POM ->
{
return Response.status(200).type(MediaType.APPLICATION_XML).entity(releasePomService.find(releaseContext)).build();
}
}
throw new BadRequestException();
}
}

View File

@ -1,7 +1,9 @@
package dev.dinauer.maven.jpa.maven;
package dev.dinauer.maven.maven.core.release;
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import dev.dinauer.maven.maven.core.ArtifactId;
import dev.dinauer.maven.maven.core.Version;
import dev.dinauer.maven.user.User;
import jakarta.persistence.*;
@ -11,8 +13,8 @@ import java.util.List;
import java.util.Optional;
@Entity
@Table(name = "version")
public class Version
@Table(name = "release_version")
public class ReleaseVersion implements Version
{
@Id
@GeneratedValue(strategy = GenerationType.UUID)
@ -31,13 +33,13 @@ public class Version
@JsonBackReference
private ArtifactId artifact;
@OneToMany(mappedBy = "version", cascade = CascadeType.ALL)
@OneToMany(mappedBy = "releaseVersion", cascade = CascadeType.ALL)
@JsonManagedReference
private List<Jar> jars = new ArrayList<>();
private List<ReleaseJar> releaseJars = new ArrayList<>();
@OneToOne(mappedBy = "version", cascade = CascadeType.ALL)
@OneToOne(mappedBy = "releaseVersion", cascade = CascadeType.ALL)
@JsonManagedReference
private Pom pom;
private ReleasePom releasePom;
@Column(name = "last_pulled")
private ZonedDateTime lastPulled;
@ -56,7 +58,7 @@ public class Version
return id;
}
public Version setId(String id)
public ReleaseVersion setId(String id)
{
this.id = id;
return this;
@ -67,7 +69,7 @@ public class Version
return groupId;
}
public Version setGroupId(String groupId)
public ReleaseVersion setGroupId(String groupId)
{
this.groupId = groupId;
return this;
@ -78,7 +80,7 @@ public class Version
return artifactId;
}
public Version setArtifactId(String artifactId)
public ReleaseVersion setArtifactId(String artifactId)
{
this.artifactId = artifactId;
return this;
@ -89,7 +91,7 @@ public class Version
return version;
}
public Version setVersion(String version)
public ReleaseVersion setVersion(String version)
{
this.version = version;
return this;
@ -100,31 +102,31 @@ public class Version
return artifact;
}
public Version setArtifact(ArtifactId artifactId)
public ReleaseVersion setArtifact(ArtifactId artifactId)
{
this.artifact = artifactId;
return this;
}
public List<Jar> getJars()
public List<ReleaseJar> getJars()
{
return jars;
return releaseJars;
}
public Version setJars(List<Jar> jars)
public ReleaseVersion setJars(List<ReleaseJar> releaseJars)
{
this.jars = jars;
this.releaseJars = releaseJars;
return this;
}
public Pom getPom()
public ReleasePom getPom()
{
return pom;
return releasePom;
}
public Version setPom(Pom pom)
public ReleaseVersion setPom(ReleasePom releasePom)
{
this.pom = pom;
this.releasePom = releasePom;
return this;
}
@ -133,7 +135,7 @@ public class Version
return lastPulled;
}
public Version setLastPulled(ZonedDateTime lastPulled)
public ReleaseVersion setLastPulled(ZonedDateTime lastPulled)
{
this.lastPulled = lastPulled;
return this;
@ -144,7 +146,7 @@ public class Version
return uploadedBy;
}
public Version setUploadedBy(String uploadedBy)
public ReleaseVersion setUploadedBy(String uploadedBy)
{
this.uploadedBy = uploadedBy;
return this;
@ -155,7 +157,7 @@ public class Version
return uploadUser;
}
public Version setUploadAccount(User uploadUser)
public ReleaseVersion setUploadAccount(User uploadUser)
{
this.uploadUser = uploadUser;
return this;
@ -166,7 +168,7 @@ public class Version
return pullCount;
}
public Version setPullCount(int pullCount)
public ReleaseVersion setPullCount(int pullCount)
{
this.pullCount = pullCount;
return this;
@ -177,15 +179,21 @@ public class Version
pullCount = pullCount + 1;
}
public Optional<Jar> getJarByFilename(String filename)
public Optional<ReleaseJar> getJarByFilename(String filename)
{
for (Jar jar : jars)
for (ReleaseJar releaseJar : releaseJars)
{
if (jar.getFilename().equals(filename))
if (releaseJar.getFilename().equals(filename))
{
return Optional.of(jar);
return Optional.of(releaseJar);
}
}
return Optional.empty();
}
@Override
public boolean isSnapshot()
{
return false;
}
}

View File

@ -0,0 +1,29 @@
package dev.dinauer.maven.maven.core.release;
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.ws.rs.NotFoundException;
import java.util.Map;
@ApplicationScoped
public class ReleaseVersionRepo implements PanacheRepositoryBase<ReleaseVersion, String>
{
public ReleaseVersion findByGroupIdAndArtifactIdAndVersion(String groupId, String artifactId, String version)
{
return find("version = :version AND artifact.artifactId = :artifactId AND artifact.group.groupId = :groupId", Map.ofEntries(Map.entry("groupId", groupId), Map.entry("artifactId", artifactId), Map.entry("version", version))).firstResult();
}
public ReleaseVersion requireByGroupIdAndArtifactIdAndVersion(String groupId, String artifactId, String version)
{
ReleaseVersion releaseVersion = find("version = :version AND artifact.artifactId = :artifactId AND artifact.group.groupId = :groupId", Map.ofEntries(Map.entry("groupId", groupId), Map.entry("artifactId", artifactId), Map.entry("version", version))).firstResult();
if (releaseVersion != null)
{
return releaseVersion;
}
else
{
throw new NotFoundException();
}
}
}

View File

@ -0,0 +1,33 @@
package dev.dinauer.maven.maven.core.release;
import dev.dinauer.maven.maven.core.ArtifactService;
import dev.dinauer.maven.maven.core.context.VersionContext;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
@ApplicationScoped
public class ReleaseVersionService
{
@Inject
ReleaseVersionRepo releaseVersionRepo;
@Inject
ArtifactService artifactService;
public ReleaseVersion findOrCreate(VersionContext versionContext)
{
ReleaseVersion releaseVersion = releaseVersionRepo.findByGroupIdAndArtifactIdAndVersion(versionContext.groupId(), versionContext.artifactId(), versionContext.version());
if (releaseVersion != null)
{
return releaseVersion;
}
else
{
ReleaseVersion created = new ReleaseVersion();
created.setArtifact(artifactService.findOrCreate(versionContext.groupId(), versionContext.artifactId()));
created.setVersion(versionContext.version());
releaseVersionRepo.persist(created);
return created;
}
}
}

View File

@ -0,0 +1,17 @@
package dev.dinauer.maven.maven.core.release.parser;
public class ReleaseFile
{
private String classifier;
public ReleaseFile setClassifier(String classifier)
{
this.classifier = classifier;
return this;
}
public String getClassifier()
{
return classifier;
}
}

View File

@ -0,0 +1,38 @@
package dev.dinauer.maven.maven.core.release.parser;
import dev.dinauer.maven.maven.shared.Parser;
import dev.dinauer.maven.maven.shared.ParserResult;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Strings;
public class ReleaseFileParser implements Parser<ReleaseFile>
{
private final String artifactId;
private final String version;
public ReleaseFileParser(String artifactId, String version)
{
this.version = version;
this.artifactId = artifactId;
}
@Override
public ParserResult<ReleaseFile> parse(String input)
{
String withoutGroupAndArtifact = dropArtifactIdAndVersion(input);
if (StringUtils.isBlank(withoutGroupAndArtifact))
{
return new ParserResult<>(null, new ReleaseFile());
}
return new ParserResult<>(null, new ReleaseFile().setClassifier(withoutGroupAndArtifact));
}
private String dropArtifactIdAndVersion(String input)
{
if (input.equals(String.format("%s-%s", artifactId, version)))
{
return "";
}
return Strings.CI.removeStart(input, String.format("%s-%s-", artifactId, version));
}
}

View File

@ -1,6 +1,8 @@
package dev.dinauer.maven.maven.core;
package dev.dinauer.maven.maven.core.rest;
import com.fasterxml.jackson.core.JsonProcessingException;
import dev.dinauer.maven.maven.core.MavenService;
import dev.dinauer.maven.maven.core.release.ReleaseService;
import io.quarkus.security.Authenticated;
import jakarta.inject.Inject;
import jakarta.ws.rs.*;
@ -12,19 +14,22 @@ import jakarta.ws.rs.core.Response;
public class Resource
{
@Inject
Service service;
ReleaseService releaseService;
@Inject
MavenService mavenService;
@PUT
@Path("/{path: .*}")
public Response put(@PathParam("path") String path, byte[] body)
public void put(@PathParam("path") String path, byte[] body) throws JsonProcessingException
{
return service.upload(path, body);
mavenService.upload(path, body);
}
@GET
@Path("/{path: .*}")
public Response get(@PathParam("path") String path) throws JsonProcessingException
{
return service.serve(path);
return mavenService.serve(path);
}
}

View File

@ -0,0 +1,110 @@
package dev.dinauer.maven.maven.core.snapshot;
import com.fasterxml.jackson.annotation.JsonBackReference;
import jakarta.persistence.*;
import java.util.List;
@Entity
@Table(name = "snapshot_bundle")
public class SnapshotBundle
{
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private String id;
private String date;
private String time;
@Column(name = "build_number")
private int buildNumber;
@ManyToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name = "snapshot_version_id")
@JsonBackReference
private SnapshotVersion snapshotVersion;
@OneToMany(mappedBy = "snapshotBundle")
private List<SnapshotJar> jars;
@OneToOne(mappedBy = "snapshotBundle")
private SnapshotPom pom;
public String getId()
{
return id;
}
public SnapshotBundle setId(String id)
{
this.id = id;
return this;
}
public String getDate()
{
return date;
}
public SnapshotBundle setDate(String date)
{
this.date = date;
return this;
}
public String getTime()
{
return time;
}
public SnapshotBundle setTime(String time)
{
this.time = time;
return this;
}
public int getBuildNumber()
{
return buildNumber;
}
public SnapshotBundle setBuildNumber(int buildNumber)
{
this.buildNumber = buildNumber;
return this;
}
public SnapshotVersion getSnapshotVersion()
{
return snapshotVersion;
}
public SnapshotBundle setSnapshotVersion(SnapshotVersion snapshotVersion)
{
this.snapshotVersion = snapshotVersion;
return this;
}
public List<SnapshotJar> getJars()
{
return jars;
}
public SnapshotBundle setJars(List<SnapshotJar> jars)
{
this.jars = jars;
return this;
}
public SnapshotPom getPom()
{
return pom;
}
public SnapshotBundle setPom(SnapshotPom pom)
{
this.pom = pom;
return this;
}
}

View File

@ -0,0 +1,15 @@
package dev.dinauer.maven.maven.core.snapshot;
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
import jakarta.enterprise.context.ApplicationScoped;
import java.util.Map;
@ApplicationScoped
public class SnapshotBundleRepo implements PanacheRepositoryBase<SnapshotBundle, String>
{
public SnapshotBundle findByVersionAndProperties(SnapshotVersion version, String date, String time, int buildNumber)
{
return find("snapshotVersion = :snapshotVersion AND date = :date AND time = :time AND buildNumber = :buildNumber", Map.ofEntries(Map.entry("snapshotVersion", version), Map.entry("date", date), Map.entry("time", time), Map.entry("buildNumber", buildNumber))).firstResult();
}
}

View File

@ -0,0 +1,50 @@
package dev.dinauer.maven.maven.core.snapshot;
import dev.dinauer.maven.maven.core.context.SnapshotContext;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.NotFoundException;
@ApplicationScoped
public class SnapshotBundleService
{
@Inject
SnapshotBundleRepo snapshotBundleRepo;
@Inject
SnapshotVersionService snapshotVersionService;
public SnapshotBundle findOrCreate(SnapshotContext snapshotContext)
{
SnapshotVersion snapshotVersion = snapshotVersionService.findOrCreate(snapshotContext);
SnapshotBundle snapshotBundle = snapshotBundleRepo.findByVersionAndProperties(snapshotVersion, snapshotContext.date(), snapshotContext.time(), snapshotContext.buildNumber());
if (snapshotBundle != null)
{
return snapshotBundle;
}
else
{
SnapshotBundle created = new SnapshotBundle();
created.setDate(snapshotContext.date());
created.setTime(snapshotContext.time());
created.setBuildNumber(snapshotContext.buildNumber());
created.setSnapshotVersion(snapshotVersion);
snapshotBundleRepo.persist(created);
return created;
}
}
public SnapshotBundle require(SnapshotContext snapshotContext)
{
SnapshotVersion snapshotVersion = snapshotVersionService.require(snapshotContext);
SnapshotBundle snapshotBundle = snapshotBundleRepo.findByVersionAndProperties(snapshotVersion, snapshotContext.date(), snapshotContext.time(), snapshotContext.buildNumber());
if (snapshotBundle != null)
{
return snapshotBundle;
}
else
{
throw new NotFoundException();
}
}
}

View File

@ -0,0 +1,53 @@
package dev.dinauer.maven.maven.core.snapshot;
public class SnapshotFile
{
private String date;
private String time;
private int buildNumber;
private String classifier;
public String getDate()
{
return date;
}
public SnapshotFile setDate(String date)
{
this.date = date;
return this;
}
public String getTime()
{
return time;
}
public SnapshotFile setTime(String time)
{
this.time = time;
return this;
}
public int getBuildNumber()
{
return buildNumber;
}
public SnapshotFile setBuildNumber(int buildNumber)
{
this.buildNumber = buildNumber;
return this;
}
public String getClassifier()
{
return classifier;
}
public SnapshotFile setClassifier(String classifier)
{
this.classifier = classifier;
return this;
}
}

View File

@ -0,0 +1,91 @@
package dev.dinauer.maven.maven.core.snapshot;
import jakarta.persistence.*;
@Entity
@Table(name = "snapshot_jar")
public class SnapshotJar
{
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private String id;
private String md5;
private String sha1;
@Column(columnDefinition = "bytea")
private byte[] jar;
private String classifier;
@ManyToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name = "snapshot_bundle_id")
private SnapshotBundle snapshotBundle;
public String getId()
{
return id;
}
public SnapshotJar setId(String id)
{
this.id = id;
return this;
}
public String getMd5()
{
return md5;
}
public SnapshotJar setMd5(String md5)
{
this.md5 = md5;
return this;
}
public String getSha1()
{
return sha1;
}
public SnapshotJar setSha1(String sha1)
{
this.sha1 = sha1;
return this;
}
public byte[] getJar()
{
return jar;
}
public SnapshotJar setJar(byte[] jar)
{
this.jar = jar;
return this;
}
public String getClassifier()
{
return classifier;
}
public SnapshotJar setClassifier(String classifier)
{
this.classifier = classifier;
return this;
}
public SnapshotBundle getSnapshotBundle()
{
return snapshotBundle;
}
public SnapshotJar setSnapshotBundle(SnapshotBundle snapshotBundle)
{
this.snapshotBundle = snapshotBundle;
return this;
}
}

View File

@ -0,0 +1,52 @@
package dev.dinauer.maven.maven.core.snapshot;
import dev.dinauer.maven.maven.core.context.SnapshotContext;
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.NotFoundException;
import java.util.Map;
@ApplicationScoped
public class SnapshotJarRepo implements PanacheRepositoryBase<SnapshotJar, String>
{
@Inject
SnapshotBundleService snapshotBundleService;
public SnapshotJar find(SnapshotContext snapshotContext)
{
SnapshotBundle bundle = snapshotBundleService.findOrCreate(snapshotContext);
if (snapshotContext.classifier().isPresent())
{
return find("snapshotBundle = :snapshotBundle AND classifier = :classifier", Map.ofEntries(Map.entry("snapshotBundle", bundle), Map.entry("classifier", snapshotContext.classifier().get()))).firstResult();
}
else
{
return find("snapshotBundle = :snapshotBundle AND classifier IS NULL", Map.ofEntries(Map.entry("snapshotBundle", bundle))).firstResult();
}
}
public SnapshotJar require(SnapshotContext snapshotContext)
{
SnapshotBundle bundle = snapshotBundleService.require(snapshotContext);
if (snapshotContext.classifier().isPresent())
{
SnapshotJar snapshotJar = find("snapshotBundle = :snapshotBundle AND classifier = :classifier", Map.ofEntries(Map.entry("snapshotBundle", bundle), Map.entry("classifier", snapshotContext.classifier().get()))).firstResult();
if (snapshotJar != null)
{
return snapshotJar;
}
throw new NotFoundException();
}
else
{
SnapshotJar snapshotJar = find("snapshotBundle = :snapshotBundle AND classifier IS NULL", Map.ofEntries(Map.entry("snapshotBundle", bundle))).firstResult();
if (snapshotJar != null)
{
return snapshotJar;
}
throw new NotFoundException();
}
}
}

View File

@ -0,0 +1,64 @@
package dev.dinauer.maven.maven.core.snapshot;
import dev.dinauer.maven.maven.core.context.SnapshotContext;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.WebApplicationException;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.NotImplementedException;
@ApplicationScoped
public class SnapshotJarService
{
@Inject
SnapshotBundleService snapshotBundleService;
@Inject
SnapshotJarRepo snapshotJarRepo;
public void store(SnapshotContext snapshotContext, byte[] body)
{
SnapshotJar snapshotJar = snapshotJarRepo.find(snapshotContext);
if (snapshotJar == null)
{
create(snapshotContext, body);
}
else
{
throw new WebApplicationException(501);
}
}
public byte[] find(SnapshotContext snapshotContext)
{
return snapshotJarRepo.require(snapshotContext).getJar();
}
public String findHash(SnapshotContext snapshotContext)
{
switch (snapshotContext.extensions().hashExt().orElseThrow())
{
case SHA1 ->
{
return snapshotJarRepo.require(snapshotContext).getSha1();
}
case MD5 ->
{
return snapshotJarRepo.require(snapshotContext).getMd5();
}
}
throw new BadRequestException();
}
private void create(SnapshotContext snapshotContext, byte[] content)
{
SnapshotJar snapshotJar = new SnapshotJar();
snapshotJar.setSnapshotBundle(snapshotBundleService.findOrCreate(snapshotContext));
snapshotJar.setJar(content);
snapshotJar.setMd5(DigestUtils.md5Hex(content));
snapshotJar.setSha1(DigestUtils.sha1Hex(content));
snapshotJar.setClassifier(snapshotContext.classifier().orElse(null));
snapshotJarRepo.persist(snapshotJar);
}
}

View File

@ -0,0 +1,81 @@
package dev.dinauer.maven.maven.core.snapshot;
import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.persistence.*;
@Entity
@Table(name = "snapshot_pom")
public class SnapshotPom
{
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private String id;
private String md5;
private String sha1;
@JsonIgnore
@Column(columnDefinition = "TEXT")
@Basic(fetch = FetchType.LAZY)
private String pom;
@OneToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name = "snapshot_bundle_id")
private SnapshotBundle snapshotBundle;
public String getId()
{
return id;
}
public SnapshotPom setId(String id)
{
this.id = id;
return this;
}
public String getMd5()
{
return md5;
}
public SnapshotPom setMd5(String md5)
{
this.md5 = md5;
return this;
}
public String getSha1()
{
return sha1;
}
public SnapshotPom setSha1(String sha1)
{
this.sha1 = sha1;
return this;
}
public String getPom()
{
return pom;
}
public SnapshotPom setPom(String pom)
{
this.pom = pom;
return this;
}
public SnapshotBundle getSnapshotBundle()
{
return snapshotBundle;
}
public SnapshotPom setSnapshotBundle(SnapshotBundle snapshotBundle)
{
this.snapshotBundle = snapshotBundle;
return this;
}
}

View File

@ -0,0 +1,31 @@
package dev.dinauer.maven.maven.core.snapshot;
import dev.dinauer.maven.maven.core.release.ReleasePom;
import dev.dinauer.maven.maven.core.release.ReleaseVersion;
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.ws.rs.NotFoundException;
import java.util.Map;
@ApplicationScoped
public class SnapshotPomRepo implements PanacheRepositoryBase<SnapshotPom, String>
{
public SnapshotPom find(SnapshotBundle bundle)
{
return find("snapshotBundle = :snapshotBundle", Map.ofEntries(Map.entry("snapshotBundle", bundle))).firstResult();
}
public SnapshotPom require(SnapshotBundle bundle)
{
SnapshotPom snapshotPom = find("snapshotBundle = :bundle", Map.ofEntries(Map.entry("bundle", bundle))).firstResult();
if (snapshotPom != null)
{
return snapshotPom;
}
else
{
throw new NotFoundException();
}
}
}

View File

@ -0,0 +1,62 @@
package dev.dinauer.maven.maven.core.snapshot;
import dev.dinauer.maven.maven.core.context.SnapshotContext;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.WebApplicationException;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.NotImplementedException;
import java.nio.charset.StandardCharsets;
@ApplicationScoped
public class SnapshotPomService
{
@Inject
SnapshotPomRepo snapshotPomRepo;
@Inject
SnapshotBundleService snapshotBundleService;
public void store(SnapshotContext snapshotContext, byte[] body)
{
SnapshotBundle snapshotBundle = snapshotBundleService.findOrCreate(snapshotContext);
SnapshotPom snapshotPom = snapshotPomRepo.find(snapshotBundle);
if (snapshotPom == null)
{
create(snapshotBundle, body);
}
else
{
throw new WebApplicationException(501);
}
}
public String find(SnapshotContext snapshotContext)
{
SnapshotBundle snapshotBundle = snapshotBundleService.require(snapshotContext);
return snapshotPomRepo.require(snapshotBundle).getPom();
}
public String findHash(SnapshotContext snapshotContext)
{
SnapshotBundle snapshotBundle = snapshotBundleService.require(snapshotContext);
SnapshotPom snapshotPom = snapshotPomRepo.require(snapshotBundle);
return switch (snapshotContext.extensions().hashExt().orElseThrow())
{
case MD5 -> snapshotPom.getMd5();
case SHA1 -> snapshotPom.getSha1();
default -> throw new IllegalStateException("Unexpected value: " + snapshotContext.extensions().hashExt());
};
}
private void create(SnapshotBundle bundle, byte[] content)
{
SnapshotPom snapshotPom = new SnapshotPom();
snapshotPom.setSnapshotBundle(bundle);
snapshotPom.setPom(new String(content, StandardCharsets.UTF_8));
snapshotPom.setMd5(DigestUtils.md5Hex(content));
snapshotPom.setSha1(DigestUtils.sha1Hex(content));
snapshotPomRepo.persist(snapshotPom);
}
}

View File

@ -0,0 +1,52 @@
package dev.dinauer.maven.maven.core.snapshot;
import dev.dinauer.maven.maven.core.context.ReleaseContext;
import dev.dinauer.maven.maven.core.context.SnapshotContext;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
@ApplicationScoped
public class SnapshotService
{
@Inject
SnapshotPomService snapshotPomService;
@Inject
SnapshotJarService snapshotJarService;
@Transactional
public void upload(SnapshotContext snapshotContext, byte[] content)
{
switch (snapshotContext.extensions().ext())
{
case JAR ->
{
snapshotJarService.store(snapshotContext, content);
}
case POM ->
{
snapshotPomService.store(snapshotContext, content);
}
}
}
public Response serve(SnapshotContext snapshotContext)
{
switch (snapshotContext.extensions().ext())
{
case JAR ->
{
return Response.status(200).type(MediaType.APPLICATION_OCTET_STREAM).entity(snapshotJarService.find(snapshotContext)).build();
}
case POM ->
{
return Response.status(200).type(MediaType.APPLICATION_XML).entity(snapshotPomService.find(snapshotContext)).build();
}
}
throw new BadRequestException();
}
}

View File

@ -0,0 +1,79 @@
package dev.dinauer.maven.maven.core.snapshot;
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import dev.dinauer.maven.maven.core.ArtifactId;
import dev.dinauer.maven.maven.core.Version;
import jakarta.persistence.*;
import java.util.List;
@Entity
@Table(name = "snapshot_version")
public class SnapshotVersion implements Version
{
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private String id;
private String version;
@ManyToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name = "artifact_id")
@JsonBackReference
private ArtifactId artifact;
@OneToMany(mappedBy = "snapshotVersion")
@JsonManagedReference
private List<SnapshotBundle> snapshotBundles;
public String getId()
{
return id;
}
public SnapshotVersion setId(String id)
{
this.id = id;
return this;
}
public String getVersion()
{
return version;
}
public SnapshotVersion setVersion(String version)
{
this.version = version;
return this;
}
public ArtifactId getArtifact()
{
return artifact;
}
public SnapshotVersion setArtifact(ArtifactId artifact)
{
this.artifact = artifact;
return this;
}
public List<SnapshotBundle> getSnapshotBundles()
{
return snapshotBundles;
}
public SnapshotVersion setSnapshotBundles(List<SnapshotBundle> snapshotBundles)
{
this.snapshotBundles = snapshotBundles;
return this;
}
@Override
public boolean isSnapshot()
{
return true;
}
}

View File

@ -0,0 +1,16 @@
package dev.dinauer.maven.maven.core.snapshot;
import dev.dinauer.maven.maven.core.ArtifactId;
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
import jakarta.enterprise.context.ApplicationScoped;
import java.util.Map;
@ApplicationScoped
public class SnapshotVersionRepo implements PanacheRepositoryBase<SnapshotVersion, String>
{
public SnapshotVersion findByArtifactAndVersion(ArtifactId artifactId, String version)
{
return find("artifact = :artifact AND version = :version", Map.ofEntries(Map.entry("artifact", artifactId), Map.entry("version", version))).firstResult();
}
}

View File

@ -0,0 +1,51 @@
package dev.dinauer.maven.maven.core.snapshot;
import dev.dinauer.maven.maven.core.ArtifactId;
import dev.dinauer.maven.maven.core.ArtifactService;
import dev.dinauer.maven.maven.core.context.SnapshotContext;
import dev.dinauer.maven.maven.core.context.VersionContext;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.NotFoundException;
@ApplicationScoped
public class SnapshotVersionService
{
@Inject
ArtifactService artifactService;
@Inject
SnapshotVersionRepo snapshotVersionRepo;
public SnapshotVersion findOrCreate(VersionContext versionContext)
{
ArtifactId artifactId = artifactService.findOrCreate(versionContext.groupId(), versionContext.artifactId());
SnapshotVersion snapshotVersion = snapshotVersionRepo.findByArtifactAndVersion(artifactId, versionContext.version());
if (snapshotVersion != null)
{
return snapshotVersion;
}
else
{
SnapshotVersion created = new SnapshotVersion();
created.setArtifact(artifactId);
created.setVersion(versionContext.version());
snapshotVersionRepo.persist(created);
return created;
}
}
public SnapshotVersion require(VersionContext versionContext)
{
ArtifactId artifactId = artifactService.require(versionContext.groupId(), versionContext.artifactId());
SnapshotVersion snapshotVersion = snapshotVersionRepo.findByArtifactAndVersion(artifactId, versionContext.version());
if (snapshotVersion != null)
{
return snapshotVersion;
}
else
{
throw new NotFoundException();
}
}
}

View File

@ -0,0 +1,50 @@
package dev.dinauer.maven.maven.core.snapshot.parser;
import dev.dinauer.maven.maven.core.snapshot.SnapshotFile;
import dev.dinauer.maven.maven.shared.Parser;
import dev.dinauer.maven.maven.shared.ParserResult;
import org.apache.commons.lang3.Strings;
import java.util.List;
public class SnapshotFileParser implements Parser<SnapshotFile>
{
private final String groupId;
private final String artifactId;
public SnapshotFileParser(String groupId, String artifactId)
{
this.groupId = groupId;
this.artifactId = artifactId;
}
@Override
public ParserResult<SnapshotFile> parse(String input)
{
String withoutGroupAndArtifact = dropGroupAndArtifact(input);
String[] sections = withoutGroupAndArtifact.split("-");
if (sections.length >= 2)
{
String[] dateTime = sections[0].split("\\.");
if (dateTime.length == 2)
{
SnapshotFile file = new SnapshotFile();
file.setDate(dateTime[0]);
file.setTime(dateTime[1]);
file.setBuildNumber(Integer.parseInt(sections[1]));
if (sections.length >= 3)
{
String classifier = String.join("-", List.of(sections).subList(2, sections.length));
file.setClassifier(classifier);
}
return new ParserResult<>(null, file);
}
}
throw new IllegalArgumentException();
}
private String dropGroupAndArtifact(String input)
{
return Strings.CI.removeStart(input, String.format("%s-%s-", groupId, artifactId));
}
}

View File

@ -0,0 +1,10 @@
package dev.dinauer.maven.maven.shared;
import dev.dinauer.maven.maven.core.model.FileExt;
import dev.dinauer.maven.maven.core.model.FileHash;
import java.util.Optional;
public record Extensions(FileExt ext, Optional<FileHash> hashExt)
{
}

View File

@ -0,0 +1,5 @@
package dev.dinauer.maven.maven.shared;
public record GenericFile<P>(Extensions extensions, P details)
{
}

View File

@ -0,0 +1,5 @@
package dev.dinauer.maven.maven.shared;
public record MavenCoordinates(String groupId, String artifactId, String version)
{
}

View File

@ -0,0 +1,6 @@
package dev.dinauer.maven.maven.shared;
public interface Parser<P>
{
ParserResult<P> parse(String input);
}

View File

@ -0,0 +1,5 @@
package dev.dinauer.maven.maven.shared;
public record ParserResult<P>(String remainder, P result)
{
}

View File

@ -5,6 +5,7 @@
%dev.quarkus.http.cors.access-control-allow-credentials=true
quarkus.http.access-log.enabled=true
# HTTP Test
%test.quarkus.http.test-port=9081
# Auth
@ -12,19 +13,22 @@ quarkus.http.auth.permission.authenticated.paths=/*
quarkus.http.auth.permission.authenticated.policy=authenticated
# Postgres
quarkus.hibernate-orm.schema-management.strategy=validate
quarkus.datasource.db-kind=postgresql
%dev.quarkus.datasource.username=postgres
%dev.quarkus.datasource.password=postgres
%dev.quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/postgres
%dev,test.quarkus.datasource.username=postgres
%dev,test.quarkus.datasource.password=postgres
%dev,test.quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/postgres
# Flyway
%dev,test.quarkus.flyway.clean-at-start=true
quarkus.flyway.migrate-at-start=true
# OIDC Server
quarkus.oidc.auth-server-url=http://localhost:8089/api/realms/maven
quarkus.oidc.client-id=backend
quarkus.oidc-client.auth-server-url=http://localhost:8089/api/realms/maven
quarkus.oidc-client.client-id=backend
quarkus.oidc-client.credentials.secret=backend
quarkus.rest-client.idp.url=http://localhost:8089/api/realms/maven
%dev,test.quarkus.flyway.clean-at-start=true
quarkus.flyway.migrate-at-start=true
# OIDC REST
quarkus.rest-client.idp.url=http://localhost:8089/api/realms/maven

View File

@ -2,100 +2,163 @@ create table group_id
(
created_at timestamp(6) with time zone,
updated_at timestamp(6) with time zone,
group_id varchar(255),
id varchar(255) not null
primary key
group_id varchar(255),
id varchar(255) not null
constraint group_id_pkey
primary key
);
create table artifact_id
(
created_at timestamp(6) with time zone,
updated_at timestamp(6) with time zone,
artifact_id varchar(255),
group_id varchar(255)
created_at timestamp(6) with time zone,
updated_at timestamp(6) with time zone,
artifact_id varchar(255),
group_id varchar(255)
constraint fkghhaxmc9rw9lhck83y9o6bp1o
references group_id,
group_id_long varchar(255),
id varchar(255) not null
primary key
id varchar(255) not null
constraint artifact_id_pkey
primary key
);
create table release_version
(
pull_count integer,
last_pulled timestamp(6) with time zone,
artifact_id varchar(255)
constraint fk7c5hahrl7ws76sidh9d1434b8
references artifact_id,
artifact_id_long varchar(255),
group_id_long varchar(255),
id varchar(255) not null
constraint release_version_pkey
primary key,
uploaded_by varchar(255),
version varchar(255)
);
create table release_jar
(
classifier varchar(255),
filename varchar(255),
id varchar(255) not null
constraint release_jar_pkey
primary key,
md5 varchar(255),
release_version_id varchar(255)
constraint fkfbi277pdpmkulym55pq12eg0g
references release_version,
sha1 varchar(255),
url varchar(255),
jar bytea
);
create table release_pom
(
filename varchar(255),
id varchar(255) not null
constraint release_pom_pkey
primary key,
md5 varchar(255),
pom text,
release_version_id varchar(255)
constraint release_pom_release_version_id_key
unique
constraint fk3ojqytildpdfl7q3lf5xri1c6
references release_version,
sha1 varchar(255),
url varchar(255)
);
create table resource
(
artifact_id varchar(255),
group_id varchar(255),
id varchar(255) not null
primary key,
version varchar(255)
group_id varchar(255),
id varchar(255) not null
constraint resource_pkey
primary key,
version varchar(255)
);
create table event
(
timestamp timestamp(6) with time zone,
accountid varchar(255),
id varchar(255) not null
primary key,
timestamp timestamp(6) with time zone,
accountid varchar(255),
id varchar(255) not null
constraint event_pkey
primary key,
resource_id varchar(255)
unique
constraint event_resource_id_key
unique
constraint fkclx5xnhdf2y3l1g3ae6ygsjmf
references resource,
type varchar(255)
type varchar(255)
constraint event_type_check
check ((type)::text = ANY ((ARRAY ['UPLOAD'::character varying, 'DELETE'::character varying])::text[]))
check ((type)::text = ANY ((ARRAY['UPLOAD'::character varying, 'DELETE'::character varying])::text[]))
);
create table snapshot_version
(
artifact_id varchar(255)
constraint fk64mje366563n82s8hy9bkfr14
references artifact_id,
id varchar(255) not null
constraint snapshot_version_pkey
primary key,
version varchar(255)
);
create table snapshot_bundle
(
build_number integer,
date varchar(255),
id varchar(255) not null
constraint snapshot_bundle_pkey
primary key,
snapshot_version_id varchar(255)
constraint fk3ve4wtmuywedyrri24dnqrpd9
references snapshot_version,
time varchar(255)
);
create table snapshot_jar
(
classifier varchar(255),
id varchar(255) not null
constraint snapshot_jar_pkey
primary key,
md5 varchar(255),
sha1 varchar(255),
snapshot_bundle_id varchar(255)
constraint fkp2vmi4ih595cbhnwiq6q1yb2n
references snapshot_bundle,
jar bytea
);
create table snapshot_pom
(
id varchar(255) not null
constraint snapshot_pom_pkey
primary key,
md5 varchar(255),
pom text,
sha1 varchar(255),
snapshot_bundle_id varchar(255)
constraint snapshot_pom_snapshot_bundle_id_key
unique
constraint fkr9gdl40j7x0ch6raju9gitfr1
references snapshot_bundle
);
create table token
(
expires_at date,
created_at timestamp(6) with time zone,
id varchar(255) not null
primary key,
name varchar(255),
token varchar(255),
user_id varchar(255)
);
create table version
(
pull_count integer,
last_pulled timestamp(6) with time zone,
artifact_id varchar(255)
constraint fklcnjnhvigubmjqng9wt7lcw71
references artifact_id,
artifact_id_long varchar(255),
group_id_long varchar(255),
id varchar(255) not null
primary key,
uploaded_by varchar(255),
version varchar(255)
);
create table jar
(
filename varchar(255),
id varchar(255) not null
primary key,
md5 varchar(255),
sha1 varchar(255),
url varchar(255),
version_id varchar(255)
constraint fkaf1ctqgugsre652w5465uhjbu
references version,
jar bytea
);
create table pom
(
filename varchar(255),
id varchar(255) not null
primary key,
md5 varchar(255),
pom text,
sha1 varchar(255),
url varchar(255),
version_id varchar(255)
unique
constraint fker1jyb1nf0vpmyv5b6ooydplu
references version
);
id varchar(255) not null
constraint token_pkey
primary key,
name varchar(255),
token varchar(255),
user_id varchar(255)
);

View File

@ -1,6 +1,6 @@
package dev.dinauer.maven.maven.core;
import dev.dinauer.maven.jpa.maven.Version;
import dev.dinauer.maven.maven.core.release.ReleaseVersion;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.security.TestSecurity;
import io.restassured.RestAssured;
@ -10,7 +10,9 @@ import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
@QuarkusTest
@ -19,32 +21,54 @@ public class ResourceTest
@Inject
Flyway flyway;
@Inject
VersionService versionService;
@BeforeEach
void before()
{
flyway.clean();
flyway.migrate();
//flyway.clean();
//flyway.migrate();
}
@Test
@TestSecurity(user = "user")
void test()
void test_01()
{
RestAssured.given().body(readFile("/jar/postgresql-42.7.9.jar")).put("/maven2/org/postgresql/postgresql/42.7.9/postgresql-42.7.9.jar");
RestAssured.given().body(readFile("/jar/postgresql-42.7.9.pom")).put("/maven2/org/postgresql/postgresql/42.7.9/postgresql-42.7.9.pom");
byte[] responseJar = RestAssured.given().get("/maven2/org/postgresql/postgresql/42.7.9/postgresql-42.7.9.jar").getBody().asByteArray();
String responsePom = RestAssured.given().get("/maven2/org/postgresql/postgresql/42.7.9/postgresql-42.7.9.pom").getBody().asString();
String responsePomMd5 = RestAssured.given().get("/maven2/org/postgresql/postgresql/42.7.9/postgresql-42.7.9.pom.md5").getBody().asString();
System.out.println("x");
}
Optional<Version> version = versionService.findOptional("org.postgresql", "postgresql", "42.7.9");
@Test
@TestSecurity(user = "user")
void test_snapshot_01() throws IOException
{
String uploadPom = new String(readFile("/jar/postgresql-42.7.9.pom").readAllBytes(), StandardCharsets.UTF_8);
RestAssured.given().body(uploadPom).put("/maven2/org/postgresql/postgresql/42.7.9-SNAPSHOT/postgresql-42.7.9-20250419.123456-1.pom").then().statusCode(204);
byte[] uploadJar = readFile("/jar/postgresql-42.7.9.jar").readAllBytes();
RestAssured.given().body(uploadJar).put("/maven2/org/postgresql/postgresql/42.7.9-SNAPSHOT/postgresql-42.7.9-20250419.123456-1.jar").then().statusCode(204);
Assertions.assertTrue(version.isPresent());
Assertions.assertTrue(responseJar.length > 0);
Assertions.assertTrue(responsePom !=null && responsePom.startsWith("<?xml"));
Assertions.assertEquals("a6b97b8d22c55f296040344dd75641b2", responsePomMd5);
String pom = RestAssured.given().get("/maven2/org/postgresql/postgresql/42.7.9-SNAPSHOT/postgresql-42.7.9-20250419.123456-1.pom").body().asString();
byte[] jar = RestAssured.given().get("/maven2/org/postgresql/postgresql/42.7.9-SNAPSHOT/postgresql-42.7.9-20250419.123456-1.jar").body().asByteArray();
Assertions.assertArrayEquals(uploadJar, jar);
Assertions.assertEquals(uploadPom, pom);
}
@Test
@TestSecurity(user = "user")
void test_release_01() throws IOException
{
String uploadPom = new String(readFile("/jar/postgresql-42.7.9.pom").readAllBytes(), StandardCharsets.UTF_8);
RestAssured.given().body(uploadPom).put("/maven2/org/postgresql/postgresql/42.7.9/postgresql-42.7.9.pom").then().statusCode(204);
byte[] uploadJar = readFile("/jar/postgresql-42.7.9.jar").readAllBytes();
RestAssured.given().body(uploadJar).put("/maven2/org/postgresql/postgresql/42.7.9/postgresql-42.7.9.jar").then().statusCode(204);
String pom = RestAssured.given().get("/maven2/org/postgresql/postgresql/42.7.9/postgresql-42.7.9.pom").body().asString();
byte[] jar = RestAssured.given().get("/maven2/org/postgresql/postgresql/42.7.9/postgresql-42.7.9.jar").body().asByteArray();
Assertions.assertArrayEquals(uploadJar, jar);
Assertions.assertEquals(uploadPom, pom);
}
@Test

View File

@ -1,5 +1,7 @@
package dev.dinauer.maven.maven.core.model;
import dev.dinauer.maven.maven.shared.Extensions;
import dev.dinauer.maven.maven.shared.ParserResult;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@ -8,13 +10,13 @@ public class ExtensionParserTest
@Test
void test_01()
{
String filename = "test.jar.md5";
String filename = "jackson-core-2.21.0-20250419.123456-1.jar.md5";
ExtensionParser.Result result = ExtensionParser.parse(filename);
ParserResult<Extensions> extensionResult = new ExtensionParser().parse(filename);
Assertions.assertEquals("test", result.base());
Assertions.assertEquals(FileExt.JAR, result.ext());
Assertions.assertEquals(FileHash.MD5, result.hashExt());
Assertions.assertEquals("jackson-core-2.21.0-20250419.123456-1", extensionResult.remainder());
Assertions.assertEquals(FileExt.JAR, extensionResult.result().ext());
Assertions.assertEquals(FileHash.MD5, extensionResult.result().hashExt());
}
@Test
@ -22,11 +24,11 @@ public class ExtensionParserTest
{
String filename = "test.pom.sha1";
ExtensionParser.Result result = ExtensionParser.parse(filename);
ParserResult<Extensions> extensionResult = new ExtensionParser().parse(filename);
Assertions.assertEquals("test", result.base());
Assertions.assertEquals(FileExt.POM, result.ext());
Assertions.assertEquals(FileHash.SHA1, result.hashExt());
Assertions.assertEquals("test", extensionResult.remainder());
Assertions.assertEquals(FileExt.POM, extensionResult.result().ext());
Assertions.assertEquals(FileHash.SHA1, extensionResult.result().hashExt());
}
@Test
@ -34,10 +36,10 @@ public class ExtensionParserTest
{
String filename = "test.jar";
ExtensionParser.Result result = ExtensionParser.parse(filename);
ParserResult<Extensions> extensionResult = new ExtensionParser().parse(filename);
Assertions.assertEquals("test", result.base());
Assertions.assertEquals(FileExt.JAR, result.ext());
Assertions.assertNull(result.hashExt());
Assertions.assertEquals("test", extensionResult.remainder());
Assertions.assertEquals(FileExt.JAR, extensionResult.result().ext());
Assertions.assertNull(extensionResult.result().hashExt());
}
}

View File

@ -1,21 +0,0 @@
package dev.dinauer.maven.maven.core.model;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class SnapshotFileParserTest
{
@Test
void test()
{
String filename = "my-library-1.0.0-20240315.142307-3-tests.jar.md5";
SnapshotFile file = SnapshotFileParser.parse("my-library", "1.0.0-SNAPSHOT", filename);
Assertions.assertEquals("20240315", file.getDate());
Assertions.assertEquals("142307", file.getTime());
Assertions.assertEquals(3, file.getBuildNumber());
Assertions.assertEquals(FileExt.JAR, file.getType());
Assertions.assertEquals(FileHash.MD5, file.getHash());
}
}

View File

@ -0,0 +1,20 @@
package dev.dinauer.maven.maven.core.release;
import com.fasterxml.jackson.core.JsonProcessingException;
import dev.dinauer.maven.maven.core.MavenService;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import org.junit.jupiter.api.Test;
@QuarkusTest
public class ReleaseServiceTest
{
@Inject
MavenService mavenService;
@Test
void test()
{
mavenService.upload("/com/fasterxml/jackson/core/jackson-databind/2.21.2/jackson-databind-2.21.2-tests.jar", null);
}
}

View File

@ -0,0 +1,20 @@
package dev.dinauer.maven.maven.core.snapshot;
import com.fasterxml.jackson.core.JsonProcessingException;
import dev.dinauer.maven.maven.core.MavenService;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import org.junit.jupiter.api.Test;
@QuarkusTest
public class SnapshotServiceTest
{
@Inject
MavenService mavenService;
@Test
void test() throws JsonProcessingException
{
mavenService.upload("/com/fasterxml/jackson/core/jackson-databind/2.21.2-SNAPSHOT/jackson-databind-2.21.2-20240315.123456-3.jar", null);
}
}

View File

@ -0,0 +1,21 @@
package dev.dinauer.maven.maven.core.snapshot.parser;
import dev.dinauer.maven.maven.core.snapshot.SnapshotFile;
import dev.dinauer.maven.maven.core.snapshot.SnapshotPom;
import dev.dinauer.maven.maven.shared.ParserResult;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class SnapshotFileParserTest
{
@Test
void test()
{
ParserResult<SnapshotFile> result = new SnapshotFileParser("jackson-core", "2.21.0").parse("jackson-core-2.21.0-20250419.123456-1-linux-x86_64");
Assertions.assertEquals("20250419", result.result().getDate());
Assertions.assertEquals("123456", result.result().getTime());
Assertions.assertEquals(1, result.result().getBuildNumber());
Assertions.assertEquals("linux-x86_64", result.result().getClassifier());
}
}