package org.ohdsi.webapi.exampleapplication; import java.util.ArrayList; import java.util.List; import javax.persistence.EntityManager; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import org.apache.commons.lang.RandomStringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.ohdsi.webapi.exampleapplication.model.Widget; import org.ohdsi.webapi.exampleapplication.repository.WidgetRepository; import org.ohdsi.webapi.job.JobExecutionResource; import org.ohdsi.webapi.job.JobTemplate; import org.ohdsi.webapi.service.AbstractDaoService; import org.ohdsi.webapi.vocabulary.Concept; import org.springframework.batch.core.JobParameters; import org.springframework.batch.core.JobParametersBuilder; import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.scope.context.ChunkContext; import org.springframework.batch.core.step.tasklet.Tasklet; import org.springframework.batch.repeat.RepeatStatus; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.transaction.TransactionException; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; /** * */ @Path("/example") public class ExampleApplicationWithJobService extends AbstractDaoService { public static final String EXAMPLE_JOB_NAME = "OhdsiExampleJob"; public static final String EXAMPLE_STEP_NAME = "OhdsiExampleStep"; @Autowired private JobTemplate jobTemplate; @Autowired private WidgetRepository widgetRepository; @Autowired private TransactionTemplate transactionTemplate; @Autowired private EntityManager em; public static class ExampleApplicationTasklet implements Tasklet { private static final Log log = LogFactory.getLog(ExampleApplicationTasklet.class); private final List<Concept> concepts; public ExampleApplicationTasklet(final List<Concept> concepts) { this.concepts = concepts; } @Override public RepeatStatus execute(final StepContribution contribution, final ChunkContext chunkContext) throws Exception { // set contextual data in JobExecutionContext chunkContext.getStepContext().getStepExecution().getJobExecution().getExecutionContext() .put("concepts", this.concepts); log.info("Tasklet execution >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); // Thread.sleep(14000L); return RepeatStatus.FINISHED; } } @POST @Produces(MediaType.APPLICATION_JSON) public JobExecutionResource queueJob() throws Exception { //Allow unique combinations of JobParameters to run in parallel. An empty JobParameters() would only allow a JobInstance to run at a time. final JobParameters jobParameters = new JobParametersBuilder().addString("param", "parameter with 250 char limit") .addLong("time", System.currentTimeMillis()).toJobParameters(); final List<Concept> concepts = new ArrayList<Concept>(); final Concept c1 = new Concept(); c1.conceptName = "c1"; final Concept c2 = new Concept(); c2.conceptName = "c2"; concepts.add(c1); concepts.add(c2); return this.jobTemplate.launchTasklet(EXAMPLE_JOB_NAME, EXAMPLE_STEP_NAME, new ExampleApplicationTasklet(concepts), jobParameters); } @GET @Path("widget") @Produces(MediaType.APPLICATION_JSON) public List<Widget> findAllWidgets() { Page<Widget> page = this.widgetRepository.findAll(new PageRequest(0, 10)); return page.getContent(); } @POST @Path("widget") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) //Wrapping in transaction (e.g. TransactionTemplate) not necessary as SimpleJpaRepository.save is annotated with @Transactional. public Widget createWidget(Widget w) { return this.widgetRepository.save(w); } private List<Widget> createWidgets() { List<Widget> widgets = new ArrayList<Widget>(); for (int x = 0; x < 20; x++) { Widget w = new Widget(); w.setName(RandomStringUtils.randomAlphanumeric(10)); widgets.add(w); } return widgets; } @POST @Path("widgets/batch") public void batchWriteWidgets() { final List<Widget> widgets = createWidgets(); this.transactionTemplate.execute(new TransactionCallback<Void>() { @Override public Void doInTransaction(TransactionStatus status) { int i = 0; for (Widget w : widgets) { em.persist(w); if (i % 5 == 0) { //5, same as the JDBC batch size //flush a batch of inserts and release memory: log.info("Flushing, clearing"); em.flush(); em.clear(); } i++; } return null; } }); log.info(String.format("Persisted %s widgets", widgets.size())); } @POST @Path("widgets") public void writeWidgets() { final List<Widget> widgets = createWidgets(); this.widgetRepository.save(widgets); log.info(String.format("Persisted %s widgets", widgets.size())); } @POST @Path("widget2") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) //@Transactional do not work with JAX-RS default config. Review caveots with @Transactional usage (proxy requirements). //Note that SimpleJpaRepository.save is annotated with @Transactional and will use default (e.g. Propagations.REQUIRES). Illustration of deviating from default propagation. public Widget createWidgetWith(final Widget w) { try { final Widget ret = getTransactionTemplateRequiresNew().execute(new TransactionCallback<Widget>() { @Override public Widget doInTransaction(final TransactionStatus status) { return widgetRepository.save(w); } }); return ret; } catch (final TransactionException e) { log.error(e.getMessage(), e); throw e; } } }