From 4cb1c5bb51e461e688440105cba47bfb7547c657 Mon Sep 17 00:00:00 2001 From: StephenL Date: Wed, 15 Apr 2026 10:11:03 +0100 Subject: [PATCH 1/2] UWS Entity package added to make it visible to the TAPServer --- src/main/resources/application.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 84a44e6..83c495d 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -30,7 +30,7 @@ quarkus.hibernate-orm.dialect=org.javastro.ivoacore.pgsphere.PgSphereDialect #ORM setup - mainly for the TAPSchema -quarkus.hibernate-orm.packages=org.ivoa.dm,org.ivoa.vodml.stdtypes,org.javastro.ivoa.tap.entities +quarkus.hibernate-orm.packages=org.ivoa.dm,org.ivoa.vodml.stdtypes,org.javastro.ivoa.tap.entities,org.javastro.ivoacore.uws.persist #%dev,%test.quarkus.hibernate-orm.database.generation=drop-and-create %prod.quarkus.hibernate-orm.database.generation=update quarkus.hibernate-orm.database.generation.create-schemas=true From 996ddaa1b5bd0d0c250b8ba60e452b6b4e4dac3a Mon Sep 17 00:00:00 2001 From: StephenL Date: Thu, 30 Apr 2026 14:41:15 +0100 Subject: [PATCH 2/2] Service added to separate transactional calls from the library and the application. TapConfiguration has the ability to use the database commented out. --- .../javastro/ivoa/tap/AsyncQueryResource.java | 9 ++- .../org/javastro/ivoa/tap/QueryResource.java | 8 +- .../javastro/ivoa/tap/TapConfiguration.java | 18 +++++ .../org/javastro/ivoa/tap/UWSService.java | 79 +++++++++++++++++++ .../ivoa/tap/quarkus/UWSResource.java | 26 ++++++ .../java/org/javastro/ivoa/tap/QueryTest.java | 1 - 6 files changed, 137 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/javastro/ivoa/tap/UWSService.java diff --git a/src/main/java/org/javastro/ivoa/tap/AsyncQueryResource.java b/src/main/java/org/javastro/ivoa/tap/AsyncQueryResource.java index 433d568..05fc844 100644 --- a/src/main/java/org/javastro/ivoa/tap/AsyncQueryResource.java +++ b/src/main/java/org/javastro/ivoa/tap/AsyncQueryResource.java @@ -7,6 +7,8 @@ import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.transaction.Transactional; import jakarta.ws.rs.*; import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.HttpHeaders; @@ -26,13 +28,18 @@ @Tag(name="TAP Query", description = "the TAP query endpoints") @ApplicationScoped @Path("/async") +@Transactional public class AsyncQueryResource extends BaseTAPResource { + + @Inject + UWSService uwsService; + //IMPL the two query endpoints are in different resources for routing purposes. @POST public Response async(@RestForm("QUERY") String query, @RestForm("LANG") String lang, @RestForm("RESPONSEFORMAT") String responseformat, @RestForm("MAXREC") Long maxrec, @RestForm("RUNID") String runid, @RestForm("UPLOAD") String upload, @Context UriInfo uriInfo) throws UWSException { - BaseUWSJob job = jobmanager.createJob(new TAPJobSpecification(query,lang,responseformat,maxrec,runid,upload)); + BaseUWSJob job = uwsService.createJob(new TAPJobSpecification(query,lang,responseformat,maxrec,runid,upload)); return Response.seeOther(asyncJobUri(job.getID())).build(); } diff --git a/src/main/java/org/javastro/ivoa/tap/QueryResource.java b/src/main/java/org/javastro/ivoa/tap/QueryResource.java index 2164df6..00be4ed 100644 --- a/src/main/java/org/javastro/ivoa/tap/QueryResource.java +++ b/src/main/java/org/javastro/ivoa/tap/QueryResource.java @@ -9,6 +9,7 @@ import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.infrastructure.Infrastructure; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; @@ -41,6 +42,9 @@ @Path("/sync") public class QueryResource extends BaseTAPResource { + @Inject + UWSService uwsService; + @ConfigProperty(name="ivoa.tap.sync-timeout-seconds", defaultValue = "5") int syncTimeoutSeconds; @@ -68,11 +72,11 @@ private Uni handleJob(String query, String lang, String resp return Uni.createFrom().deferred(() -> { final TAPJob job; try { - job = (TAPJob) jobmanager.createJob( + job = (TAPJob) uwsService.createJob( new TAPJobSpecification(query, lang, responseformat, maxrec, runid, upload) ); - jobmanager.runJob(job.getID()); // automatically run the job + uwsService.runJob(job.getID()); // automatically run the job } catch (UWSException e) { return Uni.createFrom().failure(e); } diff --git a/src/main/java/org/javastro/ivoa/tap/TapConfiguration.java b/src/main/java/org/javastro/ivoa/tap/TapConfiguration.java index 238a27b..7d03621 100644 --- a/src/main/java/org/javastro/ivoa/tap/TapConfiguration.java +++ b/src/main/java/org/javastro/ivoa/tap/TapConfiguration.java @@ -6,21 +6,26 @@ package org.javastro.ivoa.tap; +import com.fasterxml.jackson.databind.jsontype.NamedType; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Produces; import jakarta.inject.Inject; import jakarta.inject.Singleton; +import jakarta.persistence.EntityManager; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.javastro.ivoa.entities.resource.Capability; import org.javastro.ivoa.entities.vosi.capabilities.Capabilities; import org.javastro.ivoacore.common.ServiceLocator; import org.javastro.ivoacore.tap.TAPJob; +import org.javastro.ivoacore.tap.TAPJobSpecification; import org.javastro.ivoacore.tap.schema.SchemaProvider; import org.javastro.ivoacore.tap.schema.VODMLSchemaProvider; +import org.javastro.ivoacore.uws.JobFactoryAggregator; import org.javastro.ivoacore.uws.JobManager; import org.javastro.ivoacore.uws.environment.DefaultEnvironmentFactory; import org.javastro.ivoacore.uws.environment.DefaultExecutionPolicy; import org.javastro.ivoacore.uws.environment.EnvironmentFactory; +import org.javastro.ivoacore.uws.persist.DatabaseJobStore; import org.javastro.ivoacore.uws.persist.MemoryBasedJobStore; import org.javastro.ivoacore.vosi.CapabilityBuilder; import org.javastro.ivoacore.vosi.VOSIProvider; @@ -46,6 +51,9 @@ public class TapConfiguration { @Inject DataSource ds; + @Inject + EntityManager entityManager; + @ConfigProperty(name="ivoa.tap.dbCaseSensitive", defaultValue = "false") boolean isDbCaseSensitive; @@ -104,6 +112,16 @@ JobManager uws(SchemaProvider schemaProvider) { EnvironmentFactory env = new DefaultEnvironmentFactory(tmpdir); MemoryBasedJobStore store = new MemoryBasedJobStore(); + + // --- Database Job Store --- + // Create the factory aggregator with TAP job factory + /*JobFactoryAggregator factoryAgg = new JobFactoryAggregator(); + TAPJob.JobFactory tapJobFactory = new TAPJob.JobFactory(ds, schema(), env); + factoryAgg.addFactory(tapJobFactory); + + NamedType type = new NamedType(TAPJobSpecification.class, "TAP"); + DatabaseJobStore store = new DatabaseJobStore(entityManager, type, factoryAgg);*/ + DefaultExecutionPolicy policy = new DefaultExecutionPolicy(); return new JobManager(new TAPJob.JobFactory(ds, schemaProvider, env), store, policy); } diff --git a/src/main/java/org/javastro/ivoa/tap/UWSService.java b/src/main/java/org/javastro/ivoa/tap/UWSService.java new file mode 100644 index 0000000..cd857d9 --- /dev/null +++ b/src/main/java/org/javastro/ivoa/tap/UWSService.java @@ -0,0 +1,79 @@ +package org.javastro.ivoa.tap; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.transaction.Transactional; +import org.javastro.ivoa.entities.uws.ExecutionPhase; +import org.javastro.ivoa.entities.uws.Jobs; +import org.javastro.ivoa.entities.uws.ShortJobDescription; +import org.javastro.ivoacore.uws.*; +import org.javastro.ivoacore.uws.environment.execution.ParameterValue; + +import java.time.ZonedDateTime; +import java.util.List; +import java.util.Set; + +/** + * CDI service that provides transactional access to JobManager operations. + * This bridges the library JobManager with CDI transaction management. + */ +@ApplicationScoped +public class UWSService { + + @Inject + JobManager jobManager; + + @Transactional + public BaseUWSJob createJob(JobSpecification specification) throws UWSException { + return jobManager.createJob(specification); + } + + @Transactional + public ExecutionPhase setPhase(String jobId, String newPhase) throws UWSException { + return jobManager.setPhase(jobId, newPhase); + } + + @Transactional + public boolean deleteJob(String jobId) throws UWSException { + return jobManager.deleteJob(jobId); + } + + @Transactional + public void runJob(String jobId) throws UWSException { + jobManager.runJob(jobId); + } + + @Transactional + public void abortJob(String jobId) throws UWSException { + jobManager.abortJob(jobId); + } + + // Non-transactional read operations + public Set listJobIDs() throws UWSException { + return jobManager.listJobIDs(); + } + + public Jobs listJobs(String phase, ZonedDateTime after, Integer last) throws UWSException { + return jobManager.listJobs(phase, after, last); + } + + public org.javastro.ivoa.entities.uws.Job jobDetail(String jobId) throws UWSException { + return jobManager.jobDetail(jobId); + } + + public List getJobResults(String jobId) throws UWSException { + return jobManager.getJobResults(jobId); + } + + public String jobErrorDetail(String jobId) { + return jobManager.jobErrorDetail(jobId); + } + + public ZonedDateTime setDestruction(String jobId, ZonedDateTime destructionTime) throws UWSException { + return jobManager.setDestruction(jobId, destructionTime); + } + + public Long setExecutionDuration(String jobId, Long duration) throws UWSException { + return jobManager.setExecutionDuration(jobId, duration); + } +} diff --git a/src/main/java/org/javastro/ivoa/tap/quarkus/UWSResource.java b/src/main/java/org/javastro/ivoa/tap/quarkus/UWSResource.java index f4a887c..e8b0f63 100644 --- a/src/main/java/org/javastro/ivoa/tap/quarkus/UWSResource.java +++ b/src/main/java/org/javastro/ivoa/tap/quarkus/UWSResource.java @@ -8,12 +8,17 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; +import jakarta.ws.rs.FormParam; import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.UriBuilder; import org.eclipse.microprofile.openapi.annotations.tags.Tag; +import org.javastro.ivoa.entities.uws.ExecutionPhase; +import org.javastro.ivoa.tap.UWSService; import org.javastro.ivoacore.common.ServiceLocator; import org.javastro.ivoacore.uws.JobManager; +import org.javastro.ivoacore.uws.UWSException; import org.javastro.ivoacore.uws.webapi.BaseUWSResource; /* @@ -29,6 +34,9 @@ public class UWSResource extends BaseUWSResource { @Inject ServiceLocator serviceLocator; + @Inject + UWSService uwsService; + @Override protected JobManager getJobManager() { return jobManager; @@ -45,4 +53,22 @@ protected Response redirectToJob(String jobid) { return Response.seeOther(urib .build()).build(); } + + @Override + public Response setPhase(@PathParam("jobid") String jobid, @FormParam("PHASE") String phase) throws UWSException { + ExecutionPhase newphase = uwsService.setPhase(jobid, phase); // Use uwsService + return redirectToJob(jobid); + } + + @Override + public Response deleteJob(@PathParam("jobid") String jobid) throws UWSException { + boolean success = uwsService.deleteJob(jobid); // Use uwsService + if (success) { + return redirectToJob(null); + } else { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("Failed to delete job " + jobid) + .build(); + } + } } diff --git a/src/test/java/org/javastro/ivoa/tap/QueryTest.java b/src/test/java/org/javastro/ivoa/tap/QueryTest.java index 41e80a7..5f7c47e 100644 --- a/src/test/java/org/javastro/ivoa/tap/QueryTest.java +++ b/src/test/java/org/javastro/ivoa/tap/QueryTest.java @@ -7,7 +7,6 @@ import io.quarkus.test.junit.QuarkusTest; -import io.restassured.RestAssured; import io.restassured.filter.log.LogDetail; import io.restassured.http.ContentType; import io.restassured.response.Response;