diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 7c0ecb3ac7fd..a7a09a32ae6c 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -1152,7 +1152,7 @@ org.jboss.weld.servlet weld-servlet-shaded - 3.1.9.Final + 4.0.0.Final @@ -1167,6 +1167,12 @@ 2.5.0 + + org.glassfish.jersey.inject + jersey-cdi2-se + 2.28 + + org.jboss.weld weld-junit5 diff --git a/dotCMS/pom.xml b/dotCMS/pom.xml index 0563719b8027..d43b6e13e719 100644 --- a/dotCMS/pom.xml +++ b/dotCMS/pom.xml @@ -1411,7 +1411,12 @@ org.jboss.weld.servlet weld-servlet-shaded - 3.1.9.Final + 4.0.0.Final + + + + org.glassfish.jersey.inject + jersey-cdi2-se diff --git a/dotCMS/src/main/java/com/dotcms/business/FactoryLocatorProducers.java b/dotCMS/src/main/java/com/dotcms/business/FactoryLocatorProducers.java deleted file mode 100644 index 0c2d4cf8fe3a..000000000000 --- a/dotCMS/src/main/java/com/dotcms/business/FactoryLocatorProducers.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.dotcms.business; - -import com.dotcms.cube.CubeJSClientFactory; -import com.dotmarketing.business.FactoryLocator; - -import javax.enterprise.context.ApplicationScoped; -import javax.enterprise.inject.Produces; - -/** - * This class is useful to include classes are not into the CDI container but - * wants to be available to be injected. - * Most of the {@link FactoryLocator} classes will be eventually here. - * @author jsanca - */ -@ApplicationScoped -public class FactoryLocatorProducers { - - - @Produces - public CubeJSClientFactory getCubeJSClientFactory() { - return FactoryLocator.getCubeJSClientFactory(); - } -} diff --git a/dotCMS/src/main/java/com/dotcms/cube/CubeJSClientFactoryImpl.java b/dotCMS/src/main/java/com/dotcms/cube/CubeJSClientFactoryImpl.java index c0adf31d92c6..a2603e2125f0 100644 --- a/dotCMS/src/main/java/com/dotcms/cube/CubeJSClientFactoryImpl.java +++ b/dotCMS/src/main/java/com/dotcms/cube/CubeJSClientFactoryImpl.java @@ -4,19 +4,22 @@ import com.dotcms.analytics.app.AnalyticsApp; import com.dotcms.analytics.helper.AnalyticsHelper; import com.dotcms.analytics.model.AccessToken; -import com.dotcms.analytics.model.AccessTokenFetchMode; import com.dotcms.exception.AnalyticsException; import com.dotmarketing.business.APILocator; import com.dotmarketing.exception.DotDataException; import com.dotmarketing.exception.DotSecurityException; import com.google.common.annotations.VisibleForTesting; import com.liferay.portal.model.User; +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.Default; /** * Factory to create {@link CubeJSClient} instances. * * @author vico */ +@Default +@ApplicationScoped public class CubeJSClientFactoryImpl implements CubeJSClientFactory { private static AnalyticsHelper analyticsHelper = AnalyticsHelper.get(); diff --git a/dotCMS/src/main/java/com/dotcms/jobs/business/api/JobQueueManagerAPIImpl.java b/dotCMS/src/main/java/com/dotcms/jobs/business/api/JobQueueManagerAPIImpl.java index f675c331d21f..34fb94052036 100644 --- a/dotCMS/src/main/java/com/dotcms/jobs/business/api/JobQueueManagerAPIImpl.java +++ b/dotCMS/src/main/java/com/dotcms/jobs/business/api/JobQueueManagerAPIImpl.java @@ -48,6 +48,8 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.context.RequestScoped; +import javax.enterprise.inject.Default; import javax.inject.Inject; /** diff --git a/dotCMS/src/main/java/com/dotcms/jobs/business/queue/JobQueueProducer.java b/dotCMS/src/main/java/com/dotcms/jobs/business/queue/JobQueueProducer.java index 1596e9fb17d6..764146d2eac3 100644 --- a/dotCMS/src/main/java/com/dotcms/jobs/business/queue/JobQueueProducer.java +++ b/dotCMS/src/main/java/com/dotcms/jobs/business/queue/JobQueueProducer.java @@ -22,8 +22,6 @@ public class JobQueueProducer { * * @return A JobQueue instance */ - @Produces - @ApplicationScoped public JobQueue produceJobQueue() { if (JOB_QUEUE_IMPLEMENTATION_TYPE.equals("postgres")) { diff --git a/dotCMS/src/main/java/com/dotcms/jobs/business/queue/PostgresJobQueue.java b/dotCMS/src/main/java/com/dotcms/jobs/business/queue/PostgresJobQueue.java index 37bb0fa9ac1e..6eae10deced7 100644 --- a/dotCMS/src/main/java/com/dotcms/jobs/business/queue/PostgresJobQueue.java +++ b/dotCMS/src/main/java/com/dotcms/jobs/business/queue/PostgresJobQueue.java @@ -7,6 +7,7 @@ import com.dotcms.jobs.business.queue.error.JobNotFoundException; import com.dotcms.jobs.business.queue.error.JobQueueDataException; import com.dotcms.jobs.business.queue.error.JobQueueException; +import com.dotcms.repackage.org.directwebremoting.guice.ApplicationScoped; import com.dotmarketing.business.APILocator; import com.dotmarketing.common.db.DotConnect; import com.dotmarketing.exception.DotDataException; @@ -28,6 +29,7 @@ import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; +import javax.enterprise.inject.Default; /** * PostgreSQL implementation of the JobQueue interface. This class provides concrete implementations @@ -53,6 +55,8 @@ * @see Job * @see JobState */ +@Default +@ApplicationScoped public class PostgresJobQueue implements JobQueue { private static final String CREATE_JOB_QUEUE_QUERY = "INSERT INTO job_queue " diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/job/JobQueueResource.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/job/JobQueueResource.java new file mode 100644 index 000000000000..b5fbdd3c9ff6 --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/job/JobQueueResource.java @@ -0,0 +1,157 @@ +package com.dotcms.rest.api.v1.job; + +import com.dotcms.jobs.business.api.JobQueueManagerAPI; +import com.dotcms.jobs.business.job.Job; +import com.dotcms.jobs.business.job.JobPaginatedResult; +import com.dotcms.rest.InitDataObject; +import com.dotcms.rest.ResponseEntityView; +import com.dotcms.rest.WebResource; +import com.dotcms.rest.annotation.NoCache; +import com.dotmarketing.business.APILocator; +import com.dotmarketing.util.Logger; +import com.liferay.portal.model.User; +import org.glassfish.jersey.media.sse.EventOutput; +import org.glassfish.jersey.media.sse.OutboundEvent; +import org.glassfish.jersey.media.sse.SseFeature; + +import javax.inject.Inject; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.*; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +@Path("/v1/jobs") +public class JobQueueResource { + + private final WebResource webResource; + private final JobQueueManagerAPI jobQueueManagerAPI; + + //@Inject + MyTestBean myTestBean; + + public JobQueueResource() { + this(new WebResource(), APILocator.getJobQueueManagerAPI()); + } + + //@Inject + public JobQueueResource(WebResource webResource, JobQueueManagerAPI jobQueueManagerAPI) { + this.webResource = webResource; + this.jobQueueManagerAPI = jobQueueManagerAPI; + } + + @POST + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + public Response createJob(@Context HttpServletRequest request, + @QueryParam("queueName") String queueName, + Map jobParameters) { + try { + InitDataObject initData = webResource.init(null, true, request, true, null); + User user = initData.getUser(); + + String jobId = jobQueueManagerAPI.createJob(queueName, jobParameters); + + return Response.ok(new ResponseEntityView<>(jobId)).build(); + } catch (Exception e) { + Logger.error(this, "Error creating job", e); + return Response.serverError().entity(new ResponseEntityView<>(e.getMessage())).build(); + } + } + + @GET + @Path("/{jobId}/status") + @Produces(MediaType.APPLICATION_JSON) + public Response getJobStatus(@Context HttpServletRequest request, @PathParam("jobId") String jobId) { + try { + InitDataObject initData = webResource.init(null, true, request, true, null); + User user = initData.getUser(); + + Job job = jobQueueManagerAPI.getJob(jobId); + Map statusInfo = Map.of( + "state", job.state(), + "progress", job.progress(), + "executionNode", job.executionNode().orElse("N/A") + ); + + return Response.ok(new ResponseEntityView<>(statusInfo)).build(); + } catch (Exception e) { + Logger.error(this, "Error getting job status", e); + return Response.serverError().entity(new ResponseEntityView<>(e.getMessage())).build(); + } + } + + @POST + @Path("/{jobId}/cancel") + @Produces(MediaType.APPLICATION_JSON) + public Response cancelJob(@Context HttpServletRequest request, @PathParam("jobId") String jobId) { + try { + InitDataObject initData = webResource.init(null, true, request, true, null); + User user = initData.getUser(); + + jobQueueManagerAPI.cancelJob(jobId); + return Response.ok(new ResponseEntityView<>("Job cancelled successfully")).build(); + } catch (Exception e) { + Logger.error(this, "Error cancelling job", e); + return Response.serverError().entity(new ResponseEntityView<>(e.getMessage())).build(); + } + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + public Response listJobs(@Context HttpServletRequest request, + @QueryParam("page") @DefaultValue("1") int page, + @QueryParam("pageSize") @DefaultValue("20") int pageSize) { + try { + System.out.println(myTestBean.sayHello()); + InitDataObject initData = webResource.init(null, true, request, true, null); + User user = initData.getUser(); + + JobPaginatedResult result = jobQueueManagerAPI.getJobs(page, pageSize); + return Response.ok(new ResponseEntityView(result)).build(); + } catch (Exception e) { + Logger.error(this, "Error listing jobs", e); + return Response.serverError().entity(new ResponseEntityView<>(e.getMessage())).build(); + } + } + + @GET + @Path("/{jobId}/monitor") + @Produces(SseFeature.SERVER_SENT_EVENTS) + @NoCache + public EventOutput monitorJob(@Context HttpServletRequest request, @PathParam("jobId") String jobId) { + EventOutput eventOutput = new EventOutput(); + try { + InitDataObject initData = webResource.init(null, true, request, true, null); + User user = initData.getUser(); + + jobQueueManagerAPI.watchJob(jobId, job -> { + try { + OutboundEvent.Builder eventBuilder = new OutboundEvent.Builder(); + eventBuilder.name("job-update"); + eventBuilder.data(Job.class, job); + eventOutput.write(eventBuilder.build()); + } catch (IOException e) { + Logger.error(this, "Error writing SSE event", e); + } + }); + + // Keep the connection open for a reasonable time (e.g., 5 minutes) + if (!eventOutput.isClosed()) { + Thread.sleep(TimeUnit.MINUTES.toMillis(5)); + } + } catch (Exception e) { + Logger.error(this, "Error monitoring job", e); + } finally { + try { + eventOutput.close(); + } catch (IOException e) { + Logger.error(this, "Error closing SSE connection", e); + } + } + return eventOutput; + } +} diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/job/MyTestBean.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/job/MyTestBean.java new file mode 100644 index 000000000000..a5dcf0d24246 --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/job/MyTestBean.java @@ -0,0 +1,7 @@ +package com.dotcms.rest.api.v1.job; + +public interface MyTestBean { + + String sayHello(); + +} diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/job/MyTestBeanImpl.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/job/MyTestBeanImpl.java new file mode 100644 index 000000000000..13ccac024af7 --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/job/MyTestBeanImpl.java @@ -0,0 +1,12 @@ +package com.dotcms.rest.api.v1.job; + +import javax.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class MyTestBeanImpl implements MyTestBean { + + public String sayHello() { + return "Hello, World!"; + } + +} diff --git a/dotCMS/src/main/java/com/dotcms/rest/config/DotRestApplication.java b/dotCMS/src/main/java/com/dotcms/rest/config/DotRestApplication.java index 38af8db95716..79ec0e004c18 100644 --- a/dotCMS/src/main/java/com/dotcms/rest/config/DotRestApplication.java +++ b/dotCMS/src/main/java/com/dotcms/rest/config/DotRestApplication.java @@ -47,7 +47,6 @@ public class DotRestApplication extends ResourceConfig { public DotRestApplication() { - register(MultiPartFeature.class). register(JacksonJaxbJsonProvider.class). registerClasses(customClasses.keySet()). diff --git a/dotCMS/src/main/webapp/WEB-INF/beans.xml b/dotCMS/src/main/webapp/WEB-INF/beans.xml index 1675ad7ab74c..b72a4692cabe 100644 --- a/dotCMS/src/main/webapp/WEB-INF/beans.xml +++ b/dotCMS/src/main/webapp/WEB-INF/beans.xml @@ -1,9 +1,13 @@ - + xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee + http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd" + version="2.0" bean-discovery-mode="all"> + + + - \ No newline at end of file + + diff --git a/dotCMS/src/test/resources/META-INF/beans.xml b/dotCMS/src/test/resources/META-INF/beans.xml index 1675ad7ab74c..5a387688145b 100644 --- a/dotCMS/src/test/resources/META-INF/beans.xml +++ b/dotCMS/src/test/resources/META-INF/beans.xml @@ -1,9 +1,11 @@ - + xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee + http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd" + version="2.0" bean-discovery-mode="all"> + - \ No newline at end of file + + diff --git a/dotcms-integration/src/test/java/com/dotcms/jobs/business/api/JobQueueManagerAPICDITest.java b/dotcms-integration/src/test/java/com/dotcms/jobs/business/api/JobQueueManagerAPICDITest.java index bc028f524dd0..d151654a34a8 100644 --- a/dotcms-integration/src/test/java/com/dotcms/jobs/business/api/JobQueueManagerAPICDITest.java +++ b/dotcms-integration/src/test/java/com/dotcms/jobs/business/api/JobQueueManagerAPICDITest.java @@ -5,16 +5,9 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertSame; -import com.dotcms.jobs.business.api.events.EventProducer; -import com.dotcms.jobs.business.api.events.RealTimeJobMonitor; -import com.dotcms.jobs.business.error.CircuitBreaker; import com.dotcms.jobs.business.error.ExponentialBackoffRetryStrategy; -import com.dotcms.jobs.business.error.RetryStrategy; -import com.dotcms.jobs.business.error.RetryStrategyProducer; import com.dotcms.jobs.business.queue.JobQueue; -import com.dotcms.jobs.business.queue.JobQueueProducer; import javax.inject.Inject; -import org.jboss.weld.bootstrap.api.helpers.RegistrySingletonProvider; import org.jboss.weld.junit5.WeldInitiator; import org.jboss.weld.junit5.WeldJunit5Extension; import org.jboss.weld.junit5.WeldSetup; @@ -29,15 +22,7 @@ public class JobQueueManagerAPICDITest { @WeldSetup - public WeldInitiator weld = WeldInitiator.of( - WeldInitiator.createWeld() - .containerId(RegistrySingletonProvider.STATIC_INSTANCE) - .beanClasses(JobQueueManagerAPIImpl.class, JobQueueConfig.class, - JobQueue.class, RetryStrategy.class, CircuitBreaker.class, - JobQueueProducer.class, JobQueueConfigProducer.class, - RetryStrategyProducer.class, RealTimeJobMonitor.class, - EventProducer.class) - ); + public WeldInitiator weld = WeldInitiator.performDefaultDiscovery(); @Inject private JobQueueManagerAPI jobQueueManagerAPI; diff --git a/dotcms-integration/src/test/java/com/dotcms/util/IntegrationTestInitService.java b/dotcms-integration/src/test/java/com/dotcms/util/IntegrationTestInitService.java index e2d589cb1ae5..fcc689ef704c 100644 --- a/dotcms-integration/src/test/java/com/dotcms/util/IntegrationTestInitService.java +++ b/dotcms-integration/src/test/java/com/dotcms/util/IntegrationTestInitService.java @@ -60,6 +60,7 @@ public void init() throws Exception { if (initCompleted.compareAndSet(false, true)) { weld = new Weld().containerId(RegistrySingletonProvider.STATIC_INSTANCE) + /* .beanClasses( JobQueueManagerAPIImpl.class, JobQueueConfig.class, @@ -71,6 +72,7 @@ public void init() throws Exception { RetryStrategyProducer.class, RealTimeJobMonitor.class, EventProducer.class) + */ .initialize(); System.setProperty(TestUtil.DOTCMS_INTEGRATION_TEST, TestUtil.DOTCMS_INTEGRATION_TEST); diff --git a/dotcms-integration/src/test/resources/META-INF/beans.xml b/dotcms-integration/src/test/resources/META-INF/beans.xml index 1675ad7ab74c..5a387688145b 100644 --- a/dotcms-integration/src/test/resources/META-INF/beans.xml +++ b/dotcms-integration/src/test/resources/META-INF/beans.xml @@ -1,9 +1,11 @@ - + xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee + http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd" + version="2.0" bean-discovery-mode="all"> + - \ No newline at end of file + +