diff --git a/algorithms-modules/algorithms-miscellaneous-2/pom.xml b/algorithms-modules/algorithms-miscellaneous-2/pom.xml index ca14533e82..e04c22680a 100644 --- a/algorithms-modules/algorithms-miscellaneous-2/pom.xml +++ b/algorithms-modules/algorithms-miscellaneous-2/pom.xml @@ -14,16 +14,6 @@ - - org.apache.commons - commons-math3 - ${commons-math3.version} - - - commons-codec - commons-codec - ${commons-codec.version} - org.projectlombok lombok diff --git a/algorithms-modules/algorithms-miscellaneous-2/src/test/java/com/baeldung/algorithms/DijkstraAlgorithmLongRunningUnitTest.java b/algorithms-modules/algorithms-miscellaneous-2/src/test/java/com/baeldung/algorithms/DijkstraAlgorithmLongRunningUnitTest.java index 7e80d335be..849eb86427 100644 --- a/algorithms-modules/algorithms-miscellaneous-2/src/test/java/com/baeldung/algorithms/DijkstraAlgorithmLongRunningUnitTest.java +++ b/algorithms-modules/algorithms-miscellaneous-2/src/test/java/com/baeldung/algorithms/DijkstraAlgorithmLongRunningUnitTest.java @@ -1,7 +1,5 @@ package com.baeldung.algorithms; - - import com.baeldung.algorithms.ga.dijkstra.Dijkstra; import com.baeldung.algorithms.ga.dijkstra.Graph; import com.baeldung.algorithms.ga.dijkstra.Node; diff --git a/apache-cxf-modules/cxf-aegis/pom.xml b/apache-cxf-modules/cxf-aegis/pom.xml index 6b9e026cdd..d013aabc65 100644 --- a/apache-cxf-modules/cxf-aegis/pom.xml +++ b/apache-cxf-modules/cxf-aegis/pom.xml @@ -20,8 +20,4 @@ - - 4.0.0 - - \ No newline at end of file diff --git a/apache-cxf-modules/cxf-introduction/pom.xml b/apache-cxf-modules/cxf-introduction/pom.xml index fdcd100cc5..5e68bf3cbf 100644 --- a/apache-cxf-modules/cxf-introduction/pom.xml +++ b/apache-cxf-modules/cxf-introduction/pom.xml @@ -48,7 +48,6 @@ - 4.0.0 4.0.0 3.0.0 diff --git a/apache-cxf-modules/cxf-jaxrs-implementation/pom.xml b/apache-cxf-modules/cxf-jaxrs-implementation/pom.xml index 8418853b1e..978c5a51ed 100644 --- a/apache-cxf-modules/cxf-jaxrs-implementation/pom.xml +++ b/apache-cxf-modules/cxf-jaxrs-implementation/pom.xml @@ -16,12 +16,12 @@ org.apache.cxf cxf-rt-frontend-jaxrs - 4.0.0 + ${cxf.version} org.apache.cxf cxf-rt-transports-http-jetty - 4.0.0 + ${cxf.version} jakarta.xml.ws diff --git a/apache-cxf-modules/cxf-spring/pom.xml b/apache-cxf-modules/cxf-spring/pom.xml index 67a61e8200..234a19eebc 100644 --- a/apache-cxf-modules/cxf-spring/pom.xml +++ b/apache-cxf-modules/cxf-spring/pom.xml @@ -115,6 +115,7 @@ + 3.1.8 5.3.25 1.6.1 1.2 diff --git a/apache-cxf-modules/pom.xml b/apache-cxf-modules/pom.xml index 63c4e16747..245a31614b 100644 --- a/apache-cxf-modules/pom.xml +++ b/apache-cxf-modules/pom.xml @@ -35,7 +35,7 @@ - 3.1.8 + 4.0.0 \ No newline at end of file diff --git a/apache-cxf-modules/sse-jaxrs/sse-jaxrs-client/pom.xml b/apache-cxf-modules/sse-jaxrs/sse-jaxrs-client/pom.xml index ce2b0059c3..b092345334 100644 --- a/apache-cxf-modules/sse-jaxrs/sse-jaxrs-client/pom.xml +++ b/apache-cxf-modules/sse-jaxrs/sse-jaxrs-client/pom.xml @@ -16,12 +16,12 @@ org.apache.cxf cxf-rt-rs-client - ${cxf-version} + ${cxf.version} org.apache.cxf cxf-rt-rs-sse - ${cxf-version} + ${cxf.version} jakarta.ws.rs @@ -60,7 +60,6 @@ - 4.0.0 3.1.0 diff --git a/aws-modules/aws-lambda-modules/lambda-function/pom.xml b/aws-modules/aws-lambda-modules/lambda-function/pom.xml index 9fff7d1d9a..8c56aaabed 100644 --- a/aws-modules/aws-lambda-modules/lambda-function/pom.xml +++ b/aws-modules/aws-lambda-modules/lambda-function/pom.xml @@ -97,7 +97,7 @@ 1.1.1 3.11.0 1.2.1 - 2.8.2 + 2.10.1 \ No newline at end of file diff --git a/core-java-modules/core-java-11-3/pom.xml b/core-java-modules/core-java-11-3/pom.xml index 0161f4dcca..cacbc9089c 100644 --- a/core-java-modules/core-java-11-3/pom.xml +++ b/core-java-modules/core-java-11-3/pom.xml @@ -46,7 +46,7 @@ 11 11 2.16.0 - 2.10 + 2.10.1 \ No newline at end of file diff --git a/core-java-modules/core-java-collections-maps-6/pom.xml b/core-java-modules/core-java-collections-maps-6/pom.xml index a0cdc07644..6d10115d31 100644 --- a/core-java-modules/core-java-collections-maps-6/pom.xml +++ b/core-java-modules/core-java-collections-maps-6/pom.xml @@ -27,7 +27,7 @@ com.google.code.gson gson - 2.8.9 + 2.10.1 org.json diff --git a/core-java-modules/core-java-collections-set/pom.xml b/core-java-modules/core-java-collections-set/pom.xml index 1fb59db991..b3cd1ec90f 100644 --- a/core-java-modules/core-java-collections-set/pom.xml +++ b/core-java-modules/core-java-collections-set/pom.xml @@ -41,7 +41,7 @@ - 2.8.5 + 2.10.1 \ No newline at end of file diff --git a/core-java-modules/core-java-lang-oop-patterns/pom.xml b/core-java-modules/core-java-lang-oop-patterns/pom.xml index 4b89584def..f0a714e911 100644 --- a/core-java-modules/core-java-lang-oop-patterns/pom.xml +++ b/core-java-modules/core-java-lang-oop-patterns/pom.xml @@ -32,7 +32,7 @@ - 2.8.2 + 2.10.1 \ No newline at end of file diff --git a/spring-batch-2/pom.xml b/spring-batch-2/pom.xml index 378191c91c..5b6a012e96 100644 --- a/spring-batch-2/pom.xml +++ b/spring-batch-2/pom.xml @@ -48,12 +48,18 @@ ${awaitility.version} test + + org.projectlombok + lombok + ${lombok.version} + 5.0.0 4.2.0 com.baeldung.batch.SpringBootBatchProcessingApplication + 1.18.28 \ No newline at end of file diff --git a/spring-batch-2/src/main/java/com/baeldung/batchreaderproperties/BatchConfiguration.java b/spring-batch-2/src/main/java/com/baeldung/batchreaderproperties/BatchConfiguration.java new file mode 100644 index 0000000000..8fe567c4ea --- /dev/null +++ b/spring-batch-2/src/main/java/com/baeldung/batchreaderproperties/BatchConfiguration.java @@ -0,0 +1,78 @@ +package com.baeldung.batchreaderproperties; + +import java.time.ZonedDateTime; +import java.util.Map; + +import org.springframework.batch.core.Job; +import org.springframework.batch.core.Step; +import org.springframework.batch.core.configuration.annotation.StepScope; +import org.springframework.batch.core.job.builder.JobBuilder; +import org.springframework.batch.core.launch.support.RunIdIncrementer; +import org.springframework.batch.core.repository.JobRepository; +import org.springframework.batch.core.step.builder.StepBuilder; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.transaction.PlatformTransactionManager; + +import com.baeldung.batchreaderproperties.job.ExpiresSoonMedicineReader; +import com.baeldung.batchreaderproperties.job.MedicineProcessor; +import com.baeldung.batchreaderproperties.job.MedicineWriter; +import com.baeldung.batchreaderproperties.model.Medicine; + +@Configuration +public class BatchConfiguration { + + @Bean + @StepScope + public ExpiresSoonMedicineReader expiresSoonMedicineReader(JdbcTemplate jdbcTemplate, @Value("#{jobParameters}") Map jobParameters) { + + ExpiresSoonMedicineReader medicineReader = new ExpiresSoonMedicineReader(jdbcTemplate); + enrichWithJobParameters(jobParameters, medicineReader); + return medicineReader; + } + + @Bean + @StepScope + public MedicineProcessor medicineProcessor(@Value("#{jobParameters}") Map jobParameters) { + MedicineProcessor medicineProcessor = new MedicineProcessor(); + enrichWithJobParameters(jobParameters, medicineProcessor); + return medicineProcessor; + } + + @Bean + @StepScope + public MedicineWriter medicineWriter(@Value("#{jobParameters}") Map jobParameters) { + MedicineWriter medicineWriter = new MedicineWriter(); + enrichWithJobParameters(jobParameters, medicineWriter); + return medicineWriter; + } + + @Bean + public Job medExpirationJob(JobRepository jobRepository, PlatformTransactionManager transactionManager, MedicineWriter medicineWriter, MedicineProcessor medicineProcessor, ExpiresSoonMedicineReader expiresSoonMedicineReader) { + Step notifyAboutExpiringMedicine = new StepBuilder("notifyAboutExpiringMedicine", jobRepository).chunk(10) + .reader(expiresSoonMedicineReader) + .processor(medicineProcessor) + .writer(medicineWriter) + .faultTolerant() + .transactionManager(transactionManager) + .build(); + + return new JobBuilder("medExpirationJob", jobRepository).incrementer(new RunIdIncrementer()) + .start(notifyAboutExpiringMedicine) + .build(); + } + + private void enrichWithJobParameters(Map jobParameters, ContainsJobParameters container) { + if (jobParameters.get(BatchConstants.TRIGGERED_DATE_TIME) != null) { + container.setTriggeredDateTime(ZonedDateTime.parse(jobParameters.get(BatchConstants.TRIGGERED_DATE_TIME) + .toString())); + } + if (jobParameters.get(BatchConstants.TRACE_ID) != null) { + container.setTraceId(jobParameters.get(BatchConstants.TRACE_ID) + .toString()); + } + } + +} diff --git a/spring-batch-2/src/main/java/com/baeldung/batchreaderproperties/BatchConstants.java b/spring-batch-2/src/main/java/com/baeldung/batchreaderproperties/BatchConstants.java new file mode 100644 index 0000000000..ac86bd223a --- /dev/null +++ b/spring-batch-2/src/main/java/com/baeldung/batchreaderproperties/BatchConstants.java @@ -0,0 +1,10 @@ +package com.baeldung.batchreaderproperties; + +public class BatchConstants { + public static final String TRIGGERED_DATE_TIME = "TRIGGERED_DATE_TIME"; + public static final String TRACE_ID = "TRACE_ID"; + public static final String ALERT_TYPE = "ALERT_TYPE"; + public static final String DEFAULT_EXPIRATION = "DEFAULT_EXPIRATION"; + public static final String SALE_STARTS_DAYS = "SALE_STARTS_DAYS"; + public static final String MEDICINE_SALE = "MEDICINE_SALE"; +} diff --git a/spring-batch-2/src/main/java/com/baeldung/batchreaderproperties/ContainsJobParameters.java b/spring-batch-2/src/main/java/com/baeldung/batchreaderproperties/ContainsJobParameters.java new file mode 100644 index 0000000000..517246ca07 --- /dev/null +++ b/spring-batch-2/src/main/java/com/baeldung/batchreaderproperties/ContainsJobParameters.java @@ -0,0 +1,11 @@ +package com.baeldung.batchreaderproperties; + +import java.time.ZonedDateTime; + +public interface ContainsJobParameters { + ZonedDateTime getTriggeredDateTime(); + String getTraceId(); + + void setTriggeredDateTime(ZonedDateTime triggeredDateTime); + void setTraceId(String traceId); +} diff --git a/spring-batch-2/src/main/java/com/baeldung/batchreaderproperties/MedExpirationBatchRunner.java b/spring-batch-2/src/main/java/com/baeldung/batchreaderproperties/MedExpirationBatchRunner.java new file mode 100644 index 0000000000..cf031727ba --- /dev/null +++ b/spring-batch-2/src/main/java/com/baeldung/batchreaderproperties/MedExpirationBatchRunner.java @@ -0,0 +1,65 @@ +package com.baeldung.batchreaderproperties; + +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.UUID; + +import org.springframework.batch.core.Job; +import org.springframework.batch.core.JobParameters; +import org.springframework.batch.core.JobParametersBuilder; +import org.springframework.batch.core.launch.JobLauncher; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Component +@EnableScheduling +public class MedExpirationBatchRunner { + + @Autowired + private Job medExpirationJob; + + @Autowired + private JobLauncher jobLauncher; + + @Value("${batch.medicine.alert_type}") + private String alertType; + + @Value("${batch.medicine.expiration.default.days}") + private long defaultExpiration; + + @Value("${batch.medicine.start.sale.default.days}") + private long saleStartDays; + + @Value("${batch.medicine.sale}") + private double medicineSale; + + @Scheduled(cron = "${batch.medicine.cron}", zone = "GMT") + public void runJob() { + ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC); + launchJob(now); + } + + public void launchJob(ZonedDateTime triggerZonedDateTime) { + try { + JobParameters jobParameters = new JobParametersBuilder().addString(BatchConstants.TRIGGERED_DATE_TIME, triggerZonedDateTime.toString()) + .addString(BatchConstants.ALERT_TYPE, alertType) + .addLong(BatchConstants.DEFAULT_EXPIRATION, defaultExpiration) + .addLong(BatchConstants.SALE_STARTS_DAYS, saleStartDays) + .addDouble(BatchConstants.MEDICINE_SALE, medicineSale) + .addString(BatchConstants.TRACE_ID, UUID.randomUUID() + .toString()) + .toJobParameters(); + + jobLauncher.run(medExpirationJob, jobParameters); + } catch (Exception e) { + log.error("Failed to run", e); + } + } + +} diff --git a/spring-batch-2/src/main/java/com/baeldung/batchreaderproperties/SpringBatchExpireMedicationApplication.java b/spring-batch-2/src/main/java/com/baeldung/batchreaderproperties/SpringBatchExpireMedicationApplication.java new file mode 100644 index 0000000000..f71dc323c7 --- /dev/null +++ b/spring-batch-2/src/main/java/com/baeldung/batchreaderproperties/SpringBatchExpireMedicationApplication.java @@ -0,0 +1,15 @@ +package com.baeldung.batchreaderproperties; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.PropertySource; + +@SpringBootApplication +@PropertySource("classpath:disable-job-autorun.properties") +public class SpringBatchExpireMedicationApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringBatchExpireMedicationApplication.class, args); + } + +} diff --git a/spring-batch-2/src/main/java/com/baeldung/batchreaderproperties/job/ExpiresSoonMedicineReader.java b/spring-batch-2/src/main/java/com/baeldung/batchreaderproperties/job/ExpiresSoonMedicineReader.java new file mode 100644 index 0000000000..821d054818 --- /dev/null +++ b/spring-batch-2/src/main/java/com/baeldung/batchreaderproperties/job/ExpiresSoonMedicineReader.java @@ -0,0 +1,84 @@ +package com.baeldung.batchreaderproperties.job; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.ZonedDateTime; +import java.util.List; +import java.util.UUID; + +import org.springframework.batch.core.JobParameters; +import org.springframework.batch.core.StepExecution; +import org.springframework.batch.core.annotation.BeforeStep; +import org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.util.ClassUtils; + +import com.baeldung.batchreaderproperties.ContainsJobParameters; +import com.baeldung.batchreaderproperties.model.Medicine; +import com.baeldung.batchreaderproperties.model.MedicineCategory; + +import jakarta.annotation.PostConstruct; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +@Getter +@Setter +@RequiredArgsConstructor +@Slf4j +public class ExpiresSoonMedicineReader extends AbstractItemCountingItemStreamItemReader implements ContainsJobParameters { + + private static final String FIND_EXPIRING_SOON_MEDICINE = "Select * from MEDICINE where EXPIRATION_DATE >= CURRENT_DATE AND EXPIRATION_DATE <= DATEADD('DAY', ?, CURRENT_DATE)"; + //common job parameters populated in bean initialization + private ZonedDateTime triggeredDateTime; + private String traceId; + //job parameter injected by Spring + @Value("#{jobParameters['DEFAULT_EXPIRATION']}") + private long defaultExpiration; + + private final JdbcTemplate jdbcTemplate; + + private List expiringMedicineList; + + @Override + protected Medicine doRead() { + if (expiringMedicineList != null && !expiringMedicineList.isEmpty()) { + return expiringMedicineList.get(getCurrentItemCount() - 1); + } + + return null; + } + + @Override + protected void doOpen() { + expiringMedicineList = jdbcTemplate.query(FIND_EXPIRING_SOON_MEDICINE, ps -> ps.setLong(1, defaultExpiration), (rs, row) -> getMedicine(rs)); + + log.info("Trace = {}. Found {} meds that expires soon", traceId, expiringMedicineList.size()); + if (!expiringMedicineList.isEmpty()) { + setMaxItemCount(expiringMedicineList.size()); + } + } + + private static Medicine getMedicine(ResultSet rs) throws SQLException { + return new Medicine(UUID.fromString(rs.getString(1)), rs.getString(2), MedicineCategory.valueOf(rs.getString(3)), rs.getTimestamp(4), rs.getDouble(5), rs.getObject(6, Double.class)); + } + + @Override + protected void doClose() { + + } + + @PostConstruct + public void init() { + setName(ClassUtils.getShortName(getClass())); + } + + @BeforeStep + public void beforeStep(StepExecution stepExecution) { + JobParameters parameters = stepExecution.getJobExecution() + .getJobParameters(); + log.info("Before step params: {}", parameters); + } +} diff --git a/spring-batch-2/src/main/java/com/baeldung/batchreaderproperties/job/MedicineProcessor.java b/spring-batch-2/src/main/java/com/baeldung/batchreaderproperties/job/MedicineProcessor.java new file mode 100644 index 0000000000..5e4e853c98 --- /dev/null +++ b/spring-batch-2/src/main/java/com/baeldung/batchreaderproperties/job/MedicineProcessor.java @@ -0,0 +1,46 @@ +package com.baeldung.batchreaderproperties.job; + +import java.sql.Timestamp; +import java.time.Duration; +import java.time.ZoneId; +import java.time.ZonedDateTime; + +import org.springframework.batch.item.ItemProcessor; +import org.springframework.beans.factory.annotation.Value; + +import com.baeldung.batchreaderproperties.ContainsJobParameters; +import com.baeldung.batchreaderproperties.model.Medicine; + +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Getter +@Setter +public class MedicineProcessor implements ItemProcessor, ContainsJobParameters { + + private ZonedDateTime triggeredDateTime; + private String traceId; + + @Value("#{jobParameters['SALE_STARTS_DAYS']}") + private long saleStartsDays; + @Value("#{jobParameters['MEDICINE_SALE']}") + private double medicineSale; + + @Override + public Medicine process(Medicine medicine) { + + final Double originalPrice = medicine.getOriginalPrice(); + final Timestamp expirationDate = medicine.getExpirationDate(); + + Duration daysToExpiration = Duration.between(ZonedDateTime.now(), ZonedDateTime.ofInstant(expirationDate.toInstant(), ZoneId.of("UTC"))); + + if (daysToExpiration.toDays() < saleStartsDays) { + medicine.setSalePrice(originalPrice * (1 - medicineSale)); + log.info("Trace = {}, calculated new sale price {} for medicine {}", traceId, medicine.getSalePrice(), medicine.getId()); + } + + return medicine; + } +} diff --git a/spring-batch-2/src/main/java/com/baeldung/batchreaderproperties/job/MedicineWriter.java b/spring-batch-2/src/main/java/com/baeldung/batchreaderproperties/job/MedicineWriter.java new file mode 100644 index 0000000000..47c1a6a831 --- /dev/null +++ b/spring-batch-2/src/main/java/com/baeldung/batchreaderproperties/job/MedicineWriter.java @@ -0,0 +1,29 @@ +package com.baeldung.batchreaderproperties.job; + +import java.time.ZonedDateTime; + +import org.springframework.batch.item.Chunk; +import org.springframework.batch.item.ItemWriter; + +import com.baeldung.batchreaderproperties.ContainsJobParameters; +import com.baeldung.batchreaderproperties.model.Medicine; + +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +@Getter +@Setter +@Slf4j +public class MedicineWriter implements ItemWriter, ContainsJobParameters { + + private ZonedDateTime triggeredDateTime; + private String traceId; + + @Override + public void write(Chunk chunk) { + chunk.forEach((medicine) -> log.info("Trace = {}. This medicine is expiring {}", traceId, medicine)); + + log.info("Finishing job started at {}", triggeredDateTime); + } +} diff --git a/spring-batch-2/src/main/java/com/baeldung/batchreaderproperties/model/Medicine.java b/spring-batch-2/src/main/java/com/baeldung/batchreaderproperties/model/Medicine.java new file mode 100644 index 0000000000..0d657f8a8b --- /dev/null +++ b/spring-batch-2/src/main/java/com/baeldung/batchreaderproperties/model/Medicine.java @@ -0,0 +1,18 @@ +package com.baeldung.batchreaderproperties.model; + +import java.sql.Timestamp; +import java.util.UUID; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class Medicine { + private UUID id; + private String name; + private MedicineCategory type; + private Timestamp expirationDate; + private Double originalPrice; + private Double salePrice; +} diff --git a/spring-batch-2/src/main/java/com/baeldung/batchreaderproperties/model/MedicineCategory.java b/spring-batch-2/src/main/java/com/baeldung/batchreaderproperties/model/MedicineCategory.java new file mode 100644 index 0000000000..6a2f9b50e1 --- /dev/null +++ b/spring-batch-2/src/main/java/com/baeldung/batchreaderproperties/model/MedicineCategory.java @@ -0,0 +1,5 @@ +package com.baeldung.batchreaderproperties.model; + +public enum MedicineCategory { + ANESTHETICS, ANTIBACTERIALS, ANTIDEPRESSANTS; +} diff --git a/spring-batch-2/src/main/resources/application.properties b/spring-batch-2/src/main/resources/application.properties index 0b8c56d3f8..bbca1d65a2 100644 --- a/spring-batch-2/src/main/resources/application.properties +++ b/spring-batch-2/src/main/resources/application.properties @@ -1 +1,7 @@ -file.input=coffee-list.csv \ No newline at end of file +file.input=coffee-list.csv +##medicine batch related properties +batch.medicine.cron=0 */1 * * * * +batch.medicine.alert_type=LOGS +batch.medicine.expiration.default.days=60 +batch.medicine.start.sale.default.days=45 +batch.medicine.sale=0.1 \ No newline at end of file diff --git a/spring-batch-2/src/main/resources/data.sql b/spring-batch-2/src/main/resources/data.sql new file mode 100644 index 0000000000..4147f20390 --- /dev/null +++ b/spring-batch-2/src/main/resources/data.sql @@ -0,0 +1,4 @@ +INSERT INTO medicine VALUES ('ec278dd3-87b9-4ad1-858f-dfe5bc34bdb5', 'Lidocaine', 'ANESTHETICS', DATEADD('DAY', 120, CURRENT_DATE), 10, null); +INSERT INTO medicine VALUES ('9d39321d-34f3-4eb7-bb9a-a69734e0e372', 'Flucloxacillin', 'ANTIBACTERIALS', DATEADD('DAY', 40, CURRENT_DATE), 20, null); +INSERT INTO medicine VALUES ('87f4ff13-de40-4c7f-95db-627f309394dd', 'Amoxicillin', 'ANTIBACTERIALS', DATEADD('DAY', 70, CURRENT_DATE), 30, null); +INSERT INTO medicine VALUES ('acd99d6a-27be-4c89-babe-0edf4dca22cb', 'Prozac', 'ANTIDEPRESSANTS', DATEADD('DAY', 30, CURRENT_DATE), 40, null); \ No newline at end of file diff --git a/spring-batch-2/src/main/resources/disable-job-autorun.properties b/spring-batch-2/src/main/resources/disable-job-autorun.properties new file mode 100644 index 0000000000..132728afcd --- /dev/null +++ b/spring-batch-2/src/main/resources/disable-job-autorun.properties @@ -0,0 +1 @@ +spring.batch.job.enabled=false \ No newline at end of file diff --git a/spring-batch-2/src/main/resources/schema-all.sql b/spring-batch-2/src/main/resources/schema-all.sql index c17b9f9749..8e04e9a0ed 100644 --- a/spring-batch-2/src/main/resources/schema-all.sql +++ b/spring-batch-2/src/main/resources/schema-all.sql @@ -5,4 +5,15 @@ CREATE TABLE coffee ( brand VARCHAR(20), origin VARCHAR(20), characteristics VARCHAR(30) +); + +DROP TABLE medicine IF EXISTS; + +CREATE TABLE medicine ( + med_id VARCHAR(36) PRIMARY KEY, + name VARCHAR(30), + type VARCHAR(30), + expiration_date TIMESTAMP, + original_price DECIMAL, + sale_price DECIMAL ); \ No newline at end of file diff --git a/spring-reactive-modules/spring-reactive-3/pom.xml b/spring-reactive-modules/spring-reactive-3/pom.xml index 17c9690157..45dd25794e 100644 --- a/spring-reactive-modules/spring-reactive-3/pom.xml +++ b/spring-reactive-modules/spring-reactive-3/pom.xml @@ -64,6 +64,11 @@ org.springframework.session spring-session-data-redis + + com.squareup.okhttp3 + mockwebserver + 4.12.0 + diff --git a/spring-reactive-modules/spring-reactive-3/src/main/java/com/baeldung/custom/deserialization/Application.java b/spring-reactive-modules/spring-reactive-3/src/main/java/com/baeldung/custom/deserialization/Application.java new file mode 100644 index 0000000000..0fcdb3a2fb --- /dev/null +++ b/spring-reactive-modules/spring-reactive-3/src/main/java/com/baeldung/custom/deserialization/Application.java @@ -0,0 +1,13 @@ +package com.baeldung.custom.deserialization; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/spring-reactive-modules/spring-reactive-3/src/main/java/com/baeldung/custom/deserialization/config/CodecCustomizerConfig.java b/spring-reactive-modules/spring-reactive-3/src/main/java/com/baeldung/custom/deserialization/config/CodecCustomizerConfig.java new file mode 100644 index 0000000000..ef3eb1e97f --- /dev/null +++ b/spring-reactive-modules/spring-reactive-3/src/main/java/com/baeldung/custom/deserialization/config/CodecCustomizerConfig.java @@ -0,0 +1,27 @@ +package com.baeldung.custom.deserialization.config; + +import org.springframework.boot.web.codec.CodecCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.MediaType; +import org.springframework.http.codec.CodecConfigurer; +import org.springframework.http.codec.json.Jackson2JsonDecoder; +import org.springframework.http.codec.json.Jackson2JsonEncoder; +import org.springframework.util.MimeType; + +import com.fasterxml.jackson.databind.ObjectMapper; + +@Configuration +public class CodecCustomizerConfig { + + @Bean + public CodecCustomizer codecCustomizer(ObjectMapper customObjectMapper) { + return configurer -> { + MimeType mimeType = MimeType.valueOf(MediaType.APPLICATION_JSON_VALUE); + CodecConfigurer.CustomCodecs customCodecs = configurer.customCodecs(); + customCodecs.register(new Jackson2JsonDecoder(customObjectMapper, mimeType)); + customCodecs.register(new Jackson2JsonEncoder(customObjectMapper, mimeType)); + }; + } + +} diff --git a/spring-reactive-modules/spring-reactive-3/src/main/java/com/baeldung/custom/deserialization/config/CustomDeserializer.java b/spring-reactive-modules/spring-reactive-3/src/main/java/com/baeldung/custom/deserialization/config/CustomDeserializer.java new file mode 100644 index 0000000000..2eeb250e0b --- /dev/null +++ b/spring-reactive-modules/spring-reactive-3/src/main/java/com/baeldung/custom/deserialization/config/CustomDeserializer.java @@ -0,0 +1,21 @@ +package com.baeldung.custom.deserialization.config; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; + +public class CustomDeserializer extends LocalDateTimeDeserializer { + @Override + public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException { + try { + return OffsetDateTime.parse(jsonParser.getText()).atZoneSameInstant(ZoneOffset.UTC).toLocalDateTime(); + } catch (Exception e) { + return super.deserialize(jsonParser, ctxt); + } + } +} diff --git a/spring-reactive-modules/spring-reactive-3/src/main/java/com/baeldung/custom/deserialization/config/CustomObjectMapper.java b/spring-reactive-modules/spring-reactive-3/src/main/java/com/baeldung/custom/deserialization/config/CustomObjectMapper.java new file mode 100644 index 0000000000..9ebd85abf8 --- /dev/null +++ b/spring-reactive-modules/spring-reactive-3/src/main/java/com/baeldung/custom/deserialization/config/CustomObjectMapper.java @@ -0,0 +1,18 @@ +package com.baeldung.custom.deserialization.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +@Configuration +public class CustomObjectMapper { + @Bean + public ObjectMapper objectMapper() { + return new ObjectMapper() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true) + .registerModule(new JavaTimeModule()); + } +} diff --git a/spring-reactive-modules/spring-reactive-3/src/main/java/com/baeldung/custom/deserialization/controller/OrderController.java b/spring-reactive-modules/spring-reactive-3/src/main/java/com/baeldung/custom/deserialization/controller/OrderController.java new file mode 100644 index 0000000000..20327333f1 --- /dev/null +++ b/spring-reactive-modules/spring-reactive-3/src/main/java/com/baeldung/custom/deserialization/controller/OrderController.java @@ -0,0 +1,37 @@ +package com.baeldung.custom.deserialization.controller; + +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +import com.baeldung.custom.deserialization.model.OrderResponse; +import com.baeldung.custom.deserialization.service.ExternalServiceV1; +import com.baeldung.custom.deserialization.service.ExternalServiceV2; + +import reactor.core.publisher.Mono; + +@RestController +public class OrderController { + + private final ExternalServiceV1 externalServiceV1; + private final ExternalServiceV2 externalServiceV2; + + public OrderController(ExternalServiceV1 externalServiceV1, ExternalServiceV2 externalServiceV2) { + this.externalServiceV1 = externalServiceV1; + this.externalServiceV2 = externalServiceV2; + } + + @GetMapping(value = "v1/order/{id}", produces = MediaType.APPLICATION_JSON_VALUE) + public final Mono searchOrderV1(@PathVariable(value = "id") int id) { + return externalServiceV1.findById(id) + .bodyToMono(OrderResponse.class); + } + + @GetMapping(value = "v2/order/{id}", produces = MediaType.APPLICATION_JSON_VALUE) + public final Mono searchOrderV2(@PathVariable(value = "id") int id) { + return externalServiceV2.findById(id) + .bodyToMono(OrderResponse.class); + } + +} diff --git a/spring-reactive-modules/spring-reactive-3/src/main/java/com/baeldung/custom/deserialization/model/OrderResponse.java b/spring-reactive-modules/spring-reactive-3/src/main/java/com/baeldung/custom/deserialization/model/OrderResponse.java new file mode 100644 index 0000000000..18d45e7dba --- /dev/null +++ b/spring-reactive-modules/spring-reactive-3/src/main/java/com/baeldung/custom/deserialization/model/OrderResponse.java @@ -0,0 +1,19 @@ +package com.baeldung.custom.deserialization.model; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; + +import lombok.Data; + +@Data +public class OrderResponse { + + private UUID orderId; + + private LocalDateTime orderDateTime; + + private List address; + + private List orderNotes; +} diff --git a/spring-reactive-modules/spring-reactive-3/src/main/java/com/baeldung/custom/deserialization/service/ExternalServiceV1.java b/spring-reactive-modules/spring-reactive-3/src/main/java/com/baeldung/custom/deserialization/service/ExternalServiceV1.java new file mode 100644 index 0000000000..34c78ae416 --- /dev/null +++ b/spring-reactive-modules/spring-reactive-3/src/main/java/com/baeldung/custom/deserialization/service/ExternalServiceV1.java @@ -0,0 +1,23 @@ +package com.baeldung.custom.deserialization.service; + +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.WebClient; + +@Component +public class ExternalServiceV1 { + + private final WebClient.Builder webclientBuilder; + + public ExternalServiceV1(WebClient.Builder webclientBuilder) { + this.webclientBuilder = webclientBuilder; + } + + public WebClient.ResponseSpec findById(int id) { + return webclientBuilder.baseUrl("http://localhost:8090/") + .build() + .get() + .uri("external/order/" + id) + .retrieve(); + } + +} diff --git a/spring-reactive-modules/spring-reactive-3/src/main/java/com/baeldung/custom/deserialization/service/ExternalServiceV2.java b/spring-reactive-modules/spring-reactive-3/src/main/java/com/baeldung/custom/deserialization/service/ExternalServiceV2.java new file mode 100644 index 0000000000..0f976a42f2 --- /dev/null +++ b/spring-reactive-modules/spring-reactive-3/src/main/java/com/baeldung/custom/deserialization/service/ExternalServiceV2.java @@ -0,0 +1,40 @@ +package com.baeldung.custom.deserialization.service; + +import java.time.LocalDateTime; + +import org.springframework.http.MediaType; +import org.springframework.http.codec.json.Jackson2JsonDecoder; +import org.springframework.http.codec.json.Jackson2JsonEncoder; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.ExchangeStrategies; +import org.springframework.web.reactive.function.client.WebClient; + +import com.baeldung.custom.deserialization.config.CustomDeserializer; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; + +@Component +public class ExternalServiceV2 { + + public WebClient.ResponseSpec findById(int id) { + + ObjectMapper objectMapper = new ObjectMapper().registerModule(new SimpleModule().addDeserializer(LocalDateTime.class, new CustomDeserializer())); + + WebClient webClient = WebClient.builder() + .baseUrl("http://localhost:8090/") + .exchangeStrategies(ExchangeStrategies.builder() + .codecs(clientDefaultCodecsConfigurer -> { + clientDefaultCodecsConfigurer.defaultCodecs() + .jackson2JsonEncoder(new Jackson2JsonEncoder(objectMapper, MediaType.APPLICATION_JSON)); + clientDefaultCodecsConfigurer.defaultCodecs() + .jackson2JsonDecoder(new Jackson2JsonDecoder(objectMapper, MediaType.APPLICATION_JSON)); + }) + .build()) + .build(); + + return webClient.get() + .uri("external/order/" + id) + .retrieve(); + } + +} diff --git a/spring-reactive-modules/spring-reactive-3/src/test/java/com/baeldung/custom/deserialization/OrderControllerIntegrationTest.java b/spring-reactive-modules/spring-reactive-3/src/test/java/com/baeldung/custom/deserialization/OrderControllerIntegrationTest.java new file mode 100644 index 0000000000..9c8bd7c0d4 --- /dev/null +++ b/spring-reactive-modules/spring-reactive-3/src/test/java/com/baeldung/custom/deserialization/OrderControllerIntegrationTest.java @@ -0,0 +1,135 @@ +package com.baeldung.custom.deserialization; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.util.UUID; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.HttpStatus; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.web.reactive.server.WebTestClient; + +import com.baeldung.custom.deserialization.model.OrderResponse; + +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; + +@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) +@TestPropertySource(properties = "server.port=8091") +@AutoConfigureWebTestClient(timeout = "100000") +class OrderControllerIntegrationTest { + + @Autowired + private WebTestClient webTestClient; + + private static MockWebServer mockExternalService; + + @BeforeAll + static void setup() throws IOException { + mockExternalService = new MockWebServer(); + mockExternalService.start(8090); + } + + @Test + void givenMockedExternalResponse_whenSearchByIdV1_thenOrderResponseShouldFailBecauseOfUnknownProperty() { + + mockExternalService.enqueue(new MockResponse().addHeader("Content-Type", "application/json; charset=utf-8") + .setBody("{\n" + " \"orderId\": \"a1b2c3d4-e5f6-4a5b-8c9d-0123456789ab\",\n" + + " \"orderDateTime\": \"2024-01-20T12:34:56\",\n" + + " \"address\": [\"123 Main St\", \"Apt 456\", \"Cityville\"],\n" + + " \"orderNotes\": [\"Special request: Handle with care\", \"Gift wrapping required\"],\n" + + " \"customerName\": \"John Doe\",\n" + " \"totalAmount\": 99.99,\n" + + " \"paymentMethod\": \"Credit Card\"\n" + " }") + .setResponseCode(HttpStatus.OK.value())); + + webTestClient.get() + .uri("v1/order/1") + .exchange() + .expectStatus() + .is5xxServerError(); + } + + @Test + void givenMockedExternalResponse_whenSearchByIdV1_thenOrderResponseShouldBeReceivedSuccessfully() { + + mockExternalService.enqueue(new MockResponse().addHeader("Content-Type", "application/json; charset=utf-8") + .setBody("{\n" + " \"orderId\": \"a1b2c3d4-e5f6-4a5b-8c9d-0123456789ab\",\n" + + " \"orderDateTime\": \"2024-01-20T12:34:56\",\n" + + " \"address\": [\"123 Main St\", \"Apt 456\", \"Cityville\"],\n" + + " \"orderNotes\": [\"Special request: Handle with care\", \"Gift wrapping required\"]\n" + + " }") + .setResponseCode(HttpStatus.OK.value())); + + OrderResponse orderResponse = webTestClient.get() + .uri("v1/order/1") + .exchange() + .expectStatus() + .isOk() + .expectBody(OrderResponse.class) + .returnResult() + .getResponseBody(); + assertEquals(UUID.fromString("a1b2c3d4-e5f6-4a5b-8c9d-0123456789ab"), orderResponse.getOrderId()); + assertEquals(LocalDateTime.of(2024, 1, 20, 12, 34, 56), orderResponse.getOrderDateTime()); + assertThat(orderResponse.getAddress()).hasSize(3); + assertThat(orderResponse.getOrderNotes()).hasSize(2); + } + + @Test + void givenMockedExternalResponse_whenSearchByIdV2_thenOrderResponseShouldFailBecauseOfUnknownProperty() { + + mockExternalService.enqueue(new MockResponse().addHeader("Content-Type", "application/json; charset=utf-8") + .setBody("{\n" + " \"orderId\": \"a1b2c3d4-e5f6-4a5b-8c9d-0123456789ab\",\n" + + " \"orderDateTime\": \"2024-01-20T12:34:56\",\n" + + " \"address\": [\"123 Main St\", \"Apt 456\", \"Cityville\"],\n" + + " \"orderNotes\": [\"Special request: Handle with care\", \"Gift wrapping required\"],\n" + + " \"customerName\": \"John Doe\",\n" + + " \"totalAmount\": 99.99,\n" + + " \"paymentMethod\": \"Credit Card\"\n" + + " }") + .setResponseCode(HttpStatus.OK.value())); + + webTestClient.get() + .uri("v2/order/1") + .exchange() + .expectStatus() + .is5xxServerError(); + } + + @Test + void givenMockedExternalResponse_whenSearchByIdV2_thenOrderResponseShouldBeReceivedSuccessfully() { + + mockExternalService.enqueue(new MockResponse().addHeader("Content-Type", "application/json; charset=utf-8") + .setBody("{\n" + " \"orderId\": \"a1b2c3d4-e5f6-4a5b-8c9d-0123456789ab\",\n" + + " \"orderDateTime\": \"2024-01-20T14:34:56+01:00\",\n" + + " \"address\": [\"123 Main St\", \"Apt 456\", \"Cityville\"],\n" + + " \"orderNotes\": [\"Special request: Handle with care\", \"Gift wrapping required\"]\n" + " }") + .setResponseCode(HttpStatus.OK.value())); + + OrderResponse orderResponse = webTestClient.get() + .uri("v2/order/1") + .exchange() + .expectStatus() + .isOk() + .expectBody(OrderResponse.class) + .returnResult() + .getResponseBody(); + assertEquals(UUID.fromString("a1b2c3d4-e5f6-4a5b-8c9d-0123456789ab"), orderResponse.getOrderId()); + assertEquals(LocalDateTime.of(2024, 1, 20, 13, 34, 56), orderResponse.getOrderDateTime()); + assertThat(orderResponse.getAddress()).hasSize(3); + assertThat(orderResponse.getOrderNotes()).hasSize(2); + } + + @AfterAll + static void tearDown() throws IOException { + mockExternalService.shutdown(); + } + +} \ No newline at end of file diff --git a/spring-web-modules/spring-mvc-basics-4/pom.xml b/spring-web-modules/spring-mvc-basics-4/pom.xml index 7e8ef257c7..b54ed01161 100644 --- a/spring-web-modules/spring-mvc-basics-4/pom.xml +++ b/spring-web-modules/spring-mvc-basics-4/pom.xml @@ -22,7 +22,6 @@ org.springframework.boot spring-boot-starter-web - 3.0.2 org.apache.tomcat.embed @@ -31,7 +30,7 @@ javax.servlet jstl - 1.2 + ${jstl.version} org.springframework.boot @@ -39,4 +38,8 @@ + + 1.2 + + \ No newline at end of file diff --git a/spring-web-modules/spring-mvc-java/pom.xml b/spring-web-modules/spring-mvc-java/pom.xml index 213d44f350..e7c66ac0ee 100644 --- a/spring-web-modules/spring-mvc-java/pom.xml +++ b/spring-web-modules/spring-mvc-java/pom.xml @@ -65,8 +65,8 @@ com.jayway.jsonpath json-path - test ${json-path.version} + test org.springframework.boot diff --git a/spring-web-modules/spring-session/pom.xml b/spring-web-modules/spring-session/pom.xml index aec64da088..119c71af55 100644 --- a/spring-web-modules/spring-session/pom.xml +++ b/spring-web-modules/spring-session/pom.xml @@ -3,7 +3,6 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - com.baeldung spring-session 1.0.0-SNAPSHOT spring-session diff --git a/spring-web-modules/spring-thymeleaf/pom.xml b/spring-web-modules/spring-thymeleaf/pom.xml index ca94d4581e..9f6c62847d 100644 --- a/spring-web-modules/spring-thymeleaf/pom.xml +++ b/spring-web-modules/spring-thymeleaf/pom.xml @@ -108,7 +108,6 @@ org.apache.maven.plugins maven-war-plugin - ${maven-war-plugin.version} false diff --git a/testing-modules/selenide/pom.xml b/testing-modules/selenide/pom.xml index 99538d2d14..791dead47d 100644 --- a/testing-modules/selenide/pom.xml +++ b/testing-modules/selenide/pom.xml @@ -17,7 +17,7 @@ com.codeborne selenide - 6.15.0 + ${selenide.version} test @@ -54,6 +54,7 @@ + 6.15.0 6.10 4.8.3 5.3.2 diff --git a/timefold-solver/pom.xml b/timefold-solver/pom.xml index a16afb9e54..8ef97c337a 100644 --- a/timefold-solver/pom.xml +++ b/timefold-solver/pom.xml @@ -36,8 +36,6 @@ - 17 - 17 1.4.0