diff --git a/quarkus-tap-lib/src/main/java/org/javastro/ivoa/quarkus/tap/BaseAsyncTAPResource.java b/quarkus-tap-lib/src/main/java/org/javastro/ivoa/quarkus/tap/BaseAsyncTAPResource.java index 3ee4155..9aea56f 100644 --- a/quarkus-tap-lib/src/main/java/org/javastro/ivoa/quarkus/tap/BaseAsyncTAPResource.java +++ b/quarkus-tap-lib/src/main/java/org/javastro/ivoa/quarkus/tap/BaseAsyncTAPResource.java @@ -6,6 +6,7 @@ package org.javastro.ivoa.quarkus.tap; +import jakarta.transaction.Transactional; import jakarta.ws.rs.*; import jakarta.ws.rs.core.*; import org.javastro.ivoa.quarkus.tap.upload.QuarkusTapUploader; @@ -46,18 +47,19 @@ protected Response redirectToJob(String jobid) { } //IMPL the two query endpoints are in different resources for routing purposes - @POST - @Consumes({MediaType.APPLICATION_FORM_URLENCODED, MediaType.MULTIPART_FORM_DATA}) - 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, MultipartFormDataInput input, @Context UriInfo uriInfo) throws UWSException { - TAPUploadCacher tapUploader = new NullUploader(); - if(upload != null && !upload.isEmpty() ) { + @POST + @Consumes({MediaType.APPLICATION_FORM_URLENCODED, MediaType.MULTIPART_FORM_DATA}) + @Transactional + 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, MultipartFormDataInput input, @Context UriInfo uriInfo) throws UWSException { + TAPUploadCacher tapUploader = new NullUploader(); + if(upload != null && !upload.isEmpty() ) { tapUploader = new QuarkusTapUploader(upload, input); - } - BaseUWSJob job = getTapHelper().getJobmanager().createJob(new TAPJobSpecification(query,lang,responseformat,maxrec,runid,tapUploader)); - return Response.seeOther(getTapHelper().asyncJobUri(job.getID())).build(); - } + } + BaseUWSJob job = getTapHelper().getJobmanager().createJob(new TAPJobSpecification(query,lang,responseformat,maxrec,runid,tapUploader)); + return Response.seeOther(getTapHelper().asyncJobUri(job.getID())).build(); + } @GET @Path("{jobid}/results/result") @@ -68,4 +70,22 @@ public RestResponse getVotable(@PathParam("jobid") String jo .header(HttpHeaders.CONTENT_DISPOSITION, "result.vot") .build(); } + +//----------------------- Need to make database modifying operations transactional ---------------------------------- +// Which means the base UWS modifying tasks need to be wrapped in a transactional override + @Override + @DELETE + @Path("{jobid}") + @Transactional + public Response deleteJob(@PathParam("jobid")String jobid) throws UWSException { + return super.deleteJob(jobid); + } + + @Override + @POST + @Path("{jobid}/phase") + @Transactional + public Response setPhase(@PathParam("jobid") String jobid, @FormParam("PHASE") String phase) throws UWSException { + return super.setPhase(jobid, phase); + } } diff --git a/quarkus-tap-lib/src/main/java/org/javastro/ivoa/quarkus/tap/BaseSyncTAPResource.java b/quarkus-tap-lib/src/main/java/org/javastro/ivoa/quarkus/tap/BaseSyncTAPResource.java index 5cae1e1..e8c02d6 100644 --- a/quarkus-tap-lib/src/main/java/org/javastro/ivoa/quarkus/tap/BaseSyncTAPResource.java +++ b/quarkus-tap-lib/src/main/java/org/javastro/ivoa/quarkus/tap/BaseSyncTAPResource.java @@ -8,6 +8,7 @@ import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.infrastructure.Infrastructure; +import jakarta.inject.Inject; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; @@ -43,6 +44,9 @@ */ public abstract class BaseSyncTAPResource { + @Inject + TAPJobService jobService; + protected static final Logger log = LoggerFactory.getLogger(BaseSyncTAPResource.class); /** @@ -91,9 +95,7 @@ protected Uni handleJob(String query, String lang, String responseformat, if(upload != null && !upload.isEmpty() ) { tapUploader = new QuarkusTapUploader(upload, input); } - job = (TAPJob) getTapHelper().getJobmanager().createJob( - new TAPJobSpecification(query, lang, responseformat, maxrec, runid, tapUploader) - ); + job = jobService.createJob(new TAPJobSpecification(query, lang, responseformat, maxrec, runid, tapUploader)); getTapHelper().getJobmanager().runJob(job.getID()); // automatically run the job } catch (UWSException e) { diff --git a/quarkus-tap-lib/src/main/java/org/javastro/ivoa/quarkus/tap/TAPJobService.java b/quarkus-tap-lib/src/main/java/org/javastro/ivoa/quarkus/tap/TAPJobService.java new file mode 100644 index 0000000..0c94953 --- /dev/null +++ b/quarkus-tap-lib/src/main/java/org/javastro/ivoa/quarkus/tap/TAPJobService.java @@ -0,0 +1,31 @@ +package org.javastro.ivoa.quarkus.tap; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.transaction.Transactional; +import org.javastro.ivoacore.tap.TAPJob; +import org.javastro.ivoacore.tap.TAPJobSpecification; +import org.javastro.ivoacore.uws.UWSException; + +/** + * TAPJobService is responsible for managing the creation of TAPJob instances + * based on provided specifications. It relies on TAPHelper to handle the + * underlying operations associated with job management. + *

+ * This service is application-scoped, ensuring a single instance is used + * throughout the application context. In a multithreaded environment, + * the service is designed to be thread-safe. + */ +@ApplicationScoped +public class TAPJobService { + + private final TAPHelper tapHelper; + + public TAPJobService(TAPHelper tapHelper) { + this.tapHelper = tapHelper; + } + + @Transactional + public TAPJob createJob(TAPJobSpecification spec) throws UWSException { + return (TAPJob) tapHelper.getJobmanager().createJob(spec); + } +} diff --git a/src/main/java/org/javastro/ivoa/tap/AsyncQueryResource.java b/src/main/java/org/javastro/ivoa/tap/AsyncQueryResource.java index 44cf132..949f6e9 100644 --- a/src/main/java/org/javastro/ivoa/tap/AsyncQueryResource.java +++ b/src/main/java/org/javastro/ivoa/tap/AsyncQueryResource.java @@ -29,5 +29,4 @@ public class AsyncQueryResource extends BaseAsyncTAPResource { protected TAPHelper getTapHelper() { return tapHelper; } - } diff --git a/src/main/java/org/javastro/ivoa/tap/QueryResource.java b/src/main/java/org/javastro/ivoa/tap/QueryResource.java index 2d7e868..f783ecb 100644 --- a/src/main/java/org/javastro/ivoa/tap/QueryResource.java +++ b/src/main/java/org/javastro/ivoa/tap/QueryResource.java @@ -26,15 +26,12 @@ @Path("sync") public class QueryResource extends BaseSyncTAPResource { - @ConfigProperty(name="ivoa.tap.sync-timeout-seconds", defaultValue = "5") int syncTimeoutSeconds; - @Inject TAPHelper tapHelper; - @Override protected TAPHelper getTapHelper() { return tapHelper; diff --git a/src/main/java/org/javastro/ivoa/tap/TapConfiguration.java b/src/main/java/org/javastro/ivoa/tap/TapConfiguration.java index 2d0f269..c667b22 100644 --- a/src/main/java/org/javastro/ivoa/tap/TapConfiguration.java +++ b/src/main/java/org/javastro/ivoa/tap/TapConfiguration.java @@ -6,23 +6,29 @@ package org.javastro.ivoa.tap; +import io.quarkus.hibernate.orm.PersistenceUnit; 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.ivoa.quarkus.tap.TAPHelper; 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.MemoryBasedJobStore; +import org.javastro.ivoacore.uws.persist.CachedJobStore; +import org.javastro.ivoacore.uws.persist.DatabaseJobStore; +import org.javastro.ivoacore.uws.persist.JobStore; import org.javastro.ivoacore.vosi.CapabilityBuilder; import org.javastro.ivoacore.vosi.VOSIProvider; @@ -47,6 +53,10 @@ public class TapConfiguration { @Inject DataSource ds; + @Inject + @PersistenceUnit("uwsstore") + EntityManager em; + @ConfigProperty(name="ivoa.tap.dbCaseSensitive", defaultValue = "false") boolean isDbCaseSensitive; @@ -104,9 +114,18 @@ JobManager uws(SchemaProvider schemaProvider) { } EnvironmentFactory env = new DefaultEnvironmentFactory(tmpdir); - MemoryBasedJobStore store = new MemoryBasedJobStore(); + + TAPJob.JobFactory tapJobFactory = new TAPJob.JobFactory(ds, schemaProvider, env); + + JobStore store = new CachedJobStore( + DatabaseJobStore.forJobType( + em, + TAPJobSpecification.class, + "TAP" + ) + ); DefaultExecutionPolicy policy = new DefaultExecutionPolicy(); - return new JobManager(new TAPJob.JobFactory(ds, schemaProvider, env), store, policy); + return new JobManager(tapJobFactory, store, policy); } @Produces diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 91330d2..bab6dcd 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -23,30 +23,34 @@ quarkus.datasource.devservices.enabled=true #fix port %dev.quarkus.datasource.devservices.port=54771 quarkus.datasource.devservices.image-name=images.dev.uksrc.org/library/postgres-pgsphere14:latest -#quarkus.datasource.devservices.image-name=postgis/postgis:14-3.5 # # PGSphere quarkus.hibernate-orm.dialect=org.javastro.ivoacore.pgsphere.PgSphereDialect +quarkus.hibernate-orm.schema-management.create-schemas=true -#ORM setup - mainly for the TAPSchema +#default PU ORM setup - mainly for the TAPSchema quarkus.hibernate-orm.packages=org.ivoa.dm,org.ivoa.vodml.stdtypes,org.javastro.ivoacore.pgsphere,org.javastro.ivoa.tap.entities -#%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 -quarkus.hibernate-orm.database.generation.halt-on-error=false quarkus.hibernate-orm.quote-identifiers.strategy = all # below is new quarkus -#quarkus.hibernate-orm.schema-management.strategy=drop-and-create -# older quarkus +quarkus.hibernate-orm.schema-management.strategy=drop-and-create +# this is for the scripts quarkus.hibernate-orm.scripts.generation=drop-and-create quarkus.hibernate-orm.scripts.generation.create-target=TAPddl.sql quarkus.hibernate-orm.scripts.generation.drop-target=TAP_drop_ddl.sql - quarkus.hibernate-orm.log.sql=true quarkus.hibernate-orm.log.bind-parameters=true +#datasource/PU ORM setup for UWS storage (TODO are the quotes necessary? The examples have them) +#if a separate database is needed then the datasource should be created and the datasource name should be used here instead of +#quarkus.datasource."uwsstore".db-kind=postgresql +quarkus.hibernate-orm."uwsstore".packages=org.javastro.ivoacore.uws.persist +quarkus.hibernate-orm."uwsstore".schema-management.strategy=drop-and-create +quarkus.hibernate-orm."uwsstore".schema-management.create-schemas=true +quarkus.hibernate-orm."uwsstore".datasource= + + #image build quarkus.container-image.builder=docker @@ -69,4 +73,9 @@ quarkus.kubernetes.ingress.path-type=Prefix #attempt to reduce logging noise from netty websocket -quarkus.log.category."io.netty.handler.codec.http.websocketx".use-parent-handlers=false \ No newline at end of file +quarkus.log.category."io.netty.handler.codec.http.websocketx".use-parent-handlers=false + +# quarkus-tap-lib contains base JAX-RS/CDI resource classes with interceptor annotations +# such as @Transactional. Index it so Quarkus can see those annotations at build time. +quarkus.index-dependency.quarkus-tap-lib.group-id=org.javastro.ivoa.servers +quarkus.index-dependency.quarkus-tap-lib.artifact-id=quarkus-tap-lib