From 65e2f5968158dc579030e3f9476c8dccdd58963d Mon Sep 17 00:00:00 2001 From: Jose Carvajal Date: Sun, 30 Jul 2017 22:27:38 +0200 Subject: [PATCH 01/50] Matchers is now deprecated in Mockito 2, it's now replaced by ArgumentMatchers --- .../baeldung/mocks/mockito/LoginControllerIntegrationTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mocks/mock-comparisons/src/test/java/org/baeldung/mocks/mockito/LoginControllerIntegrationTest.java b/mocks/mock-comparisons/src/test/java/org/baeldung/mocks/mockito/LoginControllerIntegrationTest.java index 8f8918ab22..9d47b2f5d4 100644 --- a/mocks/mock-comparisons/src/test/java/org/baeldung/mocks/mockito/LoginControllerIntegrationTest.java +++ b/mocks/mock-comparisons/src/test/java/org/baeldung/mocks/mockito/LoginControllerIntegrationTest.java @@ -8,6 +8,7 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentMatcher; +import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; @@ -120,7 +121,7 @@ public class LoginControllerIntegrationTest { .login(userForm); // complex matcher Mockito.verify(loginService) - .setCurrentUser(Mockito.argThat(new ArgumentMatcher() { + .setCurrentUser(ArgumentMatchers.argThat(new ArgumentMatcher() { @Override public boolean matches(String argument) { return argument.startsWith("foo"); From 89d89e6dcef7df1a291394cb501ab46cd246f57a Mon Sep 17 00:00:00 2001 From: Parth Karia Date: Fri, 11 Aug 2017 15:25:43 +0530 Subject: [PATCH 02/50] BAEL-243 Spring Batch using Partitioner --- .../CustomMultiResourcePartitioner.java | 77 ++++++++ .../SpringbatchPartitionConfig.java | 169 ++++++++++++++++++ .../SpringbatchPartitionerApp.java | 28 +++ 3 files changed, 274 insertions(+) create mode 100644 spring-batch/src/main/java/org/baeldung/spring_batch_intro/partitioner/CustomMultiResourcePartitioner.java create mode 100644 spring-batch/src/main/java/org/baeldung/spring_batch_intro/partitioner/SpringbatchPartitionConfig.java create mode 100644 spring-batch/src/main/java/org/baeldung/spring_batch_intro/partitioner/SpringbatchPartitionerApp.java diff --git a/spring-batch/src/main/java/org/baeldung/spring_batch_intro/partitioner/CustomMultiResourcePartitioner.java b/spring-batch/src/main/java/org/baeldung/spring_batch_intro/partitioner/CustomMultiResourcePartitioner.java new file mode 100644 index 0000000000..4cae69efbd --- /dev/null +++ b/spring-batch/src/main/java/org/baeldung/spring_batch_intro/partitioner/CustomMultiResourcePartitioner.java @@ -0,0 +1,77 @@ +/* + * Copyright 2006-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.baeldung.spring_batch_intro.partitioner; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.batch.core.partition.support.Partitioner; +import org.springframework.batch.item.ExecutionContext; +import org.springframework.core.io.Resource; +import org.springframework.util.Assert; + +public class CustomMultiResourcePartitioner implements Partitioner { + + private static final String DEFAULT_KEY_NAME = "fileName"; + + private static final String PARTITION_KEY = "partition"; + + private Resource[] resources = new Resource[0]; + + private String keyName = DEFAULT_KEY_NAME; + + /** + * The resources to assign to each partition. In Spring configuration you + * can use a pattern to select multiple resources. + * @param resources the resources to use + */ + public void setResources(Resource[] resources) { + this.resources = resources; + } + + /** + * The name of the key for the file name in each {@link ExecutionContext}. + * Defaults to "fileName". + * @param keyName the value of the key + */ + public void setKeyName(String keyName) { + this.keyName = keyName; + } + + /** + * Assign the filename of each of the injected resources to an + * {@link ExecutionContext}. + * + * @see Partitioner#partition(int) + */ + @Override + public Map partition(int gridSize) { + Map map = new HashMap(gridSize); + int i = 0, k = 1; + for (Resource resource : resources) { + ExecutionContext context = new ExecutionContext(); + Assert.state(resource.exists(), "Resource does not exist: " + resource); + context.putString(keyName, resource.getFilename()); + context.putString("opFileName", "output" + k++ + ".xml"); + + map.put(PARTITION_KEY + i, context); + i++; + } + return map; + } + +} diff --git a/spring-batch/src/main/java/org/baeldung/spring_batch_intro/partitioner/SpringbatchPartitionConfig.java b/spring-batch/src/main/java/org/baeldung/spring_batch_intro/partitioner/SpringbatchPartitionConfig.java new file mode 100644 index 0000000000..fe8339a8b4 --- /dev/null +++ b/spring-batch/src/main/java/org/baeldung/spring_batch_intro/partitioner/SpringbatchPartitionConfig.java @@ -0,0 +1,169 @@ +package org.baeldung.spring_batch_intro.partitioner; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.text.ParseException; + +import javax.sql.DataSource; + +import org.baeldung.spring_batch_intro.model.Transaction; +import org.baeldung.spring_batch_intro.service.RecordFieldSetMapper; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.Step; +import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; +import org.springframework.batch.core.configuration.annotation.JobBuilderFactory; +import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; +import org.springframework.batch.core.configuration.annotation.StepScope; +import org.springframework.batch.core.launch.JobLauncher; +import org.springframework.batch.core.launch.support.SimpleJobLauncher; +import org.springframework.batch.core.repository.JobRepository; +import org.springframework.batch.core.repository.support.JobRepositoryFactoryBean; +import org.springframework.batch.item.UnexpectedInputException; +import org.springframework.batch.item.file.FlatFileItemReader; +import org.springframework.batch.item.file.mapping.DefaultLineMapper; +import org.springframework.batch.item.file.transform.DelimitedLineTokenizer; +import org.springframework.batch.item.xml.StaxEventItemWriter; +import org.springframework.batch.support.transaction.ResourcelessTransactionManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.FileSystemResource; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.ResourcePatternResolver; +import org.springframework.core.task.TaskExecutor; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; +import org.springframework.oxm.Marshaller; +import org.springframework.oxm.jaxb.Jaxb2Marshaller; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.transaction.PlatformTransactionManager; + +@Configuration +@EnableBatchProcessing +public class SpringbatchPartitionConfig { + + @Autowired + ResourcePatternResolver resoursePatternResolver; + + @Autowired + private JobBuilderFactory jobs; + + @Autowired + private StepBuilderFactory steps; + + @Bean(name = "partitionerJob") + public Job partitionerJob() throws UnexpectedInputException, MalformedURLException, ParseException { + return jobs.get("partitionerJob") + .start(partitionStep()) + .build(); + } + + @Bean + public Step partitionStep() throws UnexpectedInputException, MalformedURLException, ParseException { + return steps.get("partitionStep") + .partitioner("slaveStep", partitioner()) + .step(slaveStep()) + .taskExecutor(taskExecutor()) + .build(); + } + + @Bean + public Step slaveStep() throws UnexpectedInputException, MalformedURLException, ParseException { + return steps.get("slaveStep") + . chunk(1) + .reader(itemReader(null)) + .writer(itemWriter(marshaller(), null)) + .build(); + } + + @Bean + public CustomMultiResourcePartitioner partitioner() { + CustomMultiResourcePartitioner partitioner = new CustomMultiResourcePartitioner(); + Resource[] resources; + try { + resources = resoursePatternResolver.getResources("file:src/main/resources/input/partitioner/*.csv"); + } catch (IOException e) { + throw new RuntimeException("I/O problems when resolving the input file pattern.", e); + } + partitioner.setResources(resources); + return partitioner; + } + + @Bean + @StepScope + public FlatFileItemReader itemReader(@Value("#{stepExecutionContext[fileName]}") String filename) throws UnexpectedInputException, ParseException { + FlatFileItemReader reader = new FlatFileItemReader(); + DelimitedLineTokenizer tokenizer = new DelimitedLineTokenizer(); + String[] tokens = { "username", "userid", "transactiondate", "amount" }; + tokenizer.setNames(tokens); + reader.setResource(new ClassPathResource("input/partitioner/" + filename)); + DefaultLineMapper lineMapper = new DefaultLineMapper(); + lineMapper.setLineTokenizer(tokenizer); + lineMapper.setFieldSetMapper(new RecordFieldSetMapper()); + reader.setLinesToSkip(1); + reader.setLineMapper(lineMapper); + return reader; + } + + @Bean(destroyMethod = "") + @StepScope + public StaxEventItemWriter itemWriter(Marshaller marshaller, @Value("#{stepExecutionContext[opFileName]}") String filename) throws MalformedURLException { + StaxEventItemWriter itemWriter = new StaxEventItemWriter<>(); + itemWriter.setMarshaller(marshaller); + itemWriter.setRootTagName("transactionRecord"); + itemWriter.setResource(new FileSystemResource("src/main/resources/output/" + filename)); + return itemWriter; + } + + @Bean + public Marshaller marshaller() { + Jaxb2Marshaller marshaller = new Jaxb2Marshaller(); + marshaller.setClassesToBeBound(new Class[] { Transaction.class }); + return marshaller; + } + + @Bean + public TaskExecutor taskExecutor() { + ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); + taskExecutor.setMaxPoolSize(5); + taskExecutor.setCorePoolSize(5); + taskExecutor.setQueueCapacity(5); + taskExecutor.afterPropertiesSet(); + return taskExecutor; + } + + private JobRepository getJobRepository() throws Exception { + JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean(); + factory.setDataSource(dataSource()); + factory.setTransactionManager(getTransactionManager()); + // JobRepositoryFactoryBean's methods Throws Generic Exception, + // it would have been better to have a specific one + factory.afterPropertiesSet(); + return (JobRepository) factory.getObject(); + } + + private DataSource dataSource() { + EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); + EmbeddedDatabase db = builder.setType(EmbeddedDatabaseType.HSQL) + .addScript("classpath:org/springframework/batch/core/schema-drop-h2.sql") + .addScript("classpath:org/springframework/batch/core/schema-h2.sql") + .build(); + return db; + } + + private PlatformTransactionManager getTransactionManager() { + return new ResourcelessTransactionManager(); + } + + public JobLauncher getJobLauncher() throws Exception { + SimpleJobLauncher jobLauncher = new SimpleJobLauncher(); + // SimpleJobLauncher's methods Throws Generic Exception, + // it would have been better to have a specific one + jobLauncher.setJobRepository(getJobRepository()); + jobLauncher.afterPropertiesSet(); + return jobLauncher; + } +} diff --git a/spring-batch/src/main/java/org/baeldung/spring_batch_intro/partitioner/SpringbatchPartitionerApp.java b/spring-batch/src/main/java/org/baeldung/spring_batch_intro/partitioner/SpringbatchPartitionerApp.java new file mode 100644 index 0000000000..1d6d922969 --- /dev/null +++ b/spring-batch/src/main/java/org/baeldung/spring_batch_intro/partitioner/SpringbatchPartitionerApp.java @@ -0,0 +1,28 @@ +package org.baeldung.spring_batch_intro.partitioner; + +import org.springframework.batch.core.Job; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.JobParameters; +import org.springframework.batch.core.launch.JobLauncher; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +public class SpringbatchPartitionerApp { + public static void main(final String[] args) { + // Spring Java config + final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(SpringbatchPartitionConfig.class); + context.refresh(); + + final JobLauncher jobLauncher = (JobLauncher) context.getBean("jobLauncher"); + final Job job = (Job) context.getBean("partitionerJob"); + System.out.println("Starting the batch job"); + try { + final JobExecution execution = jobLauncher.run(job, new JobParameters()); + System.out.println("Job Status : " + execution.getStatus()); + System.out.println("Job succeeded"); + } catch (final Exception e) { + e.printStackTrace(); + System.out.println("Job failed"); + } + } +} \ No newline at end of file From 9f9fbc4523d58ce40570f20a9dd631fda3322d41 Mon Sep 17 00:00:00 2001 From: parthkaria Date: Tue, 29 Aug 2017 17:14:13 +0530 Subject: [PATCH 03/50] BAEL-1106 Introduction to javax.measure --- core-java/pom.xml | 8 +++ .../com/baeldung/javax/measure/WaterTank.java | 26 +++++++ .../javax/measure/WaterTankTests.java | 68 +++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 core-java/src/main/java/com/baeldung/javax/measure/WaterTank.java create mode 100644 core-java/src/test/java/com/baeldung/javax/measure/WaterTankTests.java diff --git a/core-java/pom.xml b/core-java/pom.xml index 0f8a665fa1..5410ea54e2 100644 --- a/core-java/pom.xml +++ b/core-java/pom.xml @@ -212,6 +212,13 @@ streamex ${streamex.version} + + + javax.measure + jsr-275 + ${javax-measure.version} + + @@ -438,6 +445,7 @@ 1.5.1 1.13 0.6.5 + 0.9.1 1.3 diff --git a/core-java/src/main/java/com/baeldung/javax/measure/WaterTank.java b/core-java/src/main/java/com/baeldung/javax/measure/WaterTank.java new file mode 100644 index 0000000000..1ab9eee53f --- /dev/null +++ b/core-java/src/main/java/com/baeldung/javax/measure/WaterTank.java @@ -0,0 +1,26 @@ +package com.baeldung.javax.measure; + +import javax.measure.Measure; +import javax.measure.quantity.Volume; + +public class WaterTank { + + private Measure capacityMeasure; + private double capacityDouble; + + public void setCapacityMeasure(Measure capacityMeasure) { + this.capacityMeasure = capacityMeasure; + } + + public void setCapacityDouble(double capacityDouble) { + this.capacityDouble = capacityDouble; + } + + public Measure getCapacityMeasure() { + return capacityMeasure; + } + + public double getCapacityDouble() { + return capacityDouble; + } +} diff --git a/core-java/src/test/java/com/baeldung/javax/measure/WaterTankTests.java b/core-java/src/test/java/com/baeldung/javax/measure/WaterTankTests.java new file mode 100644 index 0000000000..ef54035353 --- /dev/null +++ b/core-java/src/test/java/com/baeldung/javax/measure/WaterTankTests.java @@ -0,0 +1,68 @@ +package com.baeldung.javax.measure; + +import javax.measure.Measure; +import javax.measure.converter.UnitConverter; +import javax.measure.quantity.Duration; +import javax.measure.quantity.Length; +import javax.measure.quantity.Pressure; +import javax.measure.quantity.Volume; +import static javax.measure.unit.NonSI.HOUR; +import static javax.measure.unit.NonSI.LITRE; +import static javax.measure.unit.NonSI.MILE; +import static javax.measure.unit.NonSI.MINUTE; +import javax.measure.unit.SI; +import static javax.measure.unit.SI.KILO; +import static javax.measure.unit.SI.METER; +import static javax.measure.unit.SI.NEWTON; +import static javax.measure.unit.SI.SECOND; +import javax.measure.unit.Unit; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import org.junit.Test; + +public class WaterTankTests { + + @Test + public void givenMeasure_whenGetUnitAndConvertValue_thenSuccess() { + WaterTank waterTank = new WaterTank(); + waterTank.setCapacityMeasure(Measure.valueOf(9.2, LITRE)); + assertEquals(LITRE, waterTank.getCapacityMeasure().getUnit()); + + Measure waterCapacity = waterTank.getCapacityMeasure(); + double volumeInLitre = waterCapacity.getValue().doubleValue(); + assertEquals(9.2, volumeInLitre, 0.0f); + + double volumeInMilliLitre = waterCapacity.doubleValue(SI.MILLI(LITRE)); + assertEquals(9200.0, volumeInMilliLitre, 0.0f); + + Unit Kilometer = SI.KILO(METER); + Unit Centimeter = SI.CENTI(METER); + } + + @Test + public void givenMeasure_whenAlternateMeasure_ThenGetAlternateMeasure() { + Unit PASCAL = NEWTON.divide(METER.pow(2)).alternate("Pa"); + assertTrue(Unit.valueOf("Pa").equals(PASCAL)); + } + + @Test + public void givenMeasure_whenCompoundMeasure_ThenGetCompoundMeasure() { + Unit HOUR_MINUTE_SECOND = HOUR.compound(MINUTE).compound(SECOND); + Measure duration = Measure.valueOf(12345, SECOND); + assertEquals("3h25min45s", duration.to(HOUR_MINUTE_SECOND).toString()); + } + + @Test + public void givenMiles_whenConvertToKilometer_ThenConverted() { + double distanceInMiles = 50.0; + UnitConverter mileToKilometer = MILE.getConverterTo(KILO(METER)); + double distanceInKilometers = mileToKilometer.convert(distanceInMiles); + assertEquals(80.4672, distanceInKilometers, 0.00f); + } + + @Test + public void givenSymbol_WhenCompareToSystemUnit_ThenSuccess() { + assertTrue(Unit.valueOf("kW").equals(SI.KILO(SI.WATT))); + assertTrue(Unit.valueOf("ms").equals(SI.SECOND.divide(1000))); + } +} From e573e2c43b35809f5521fa47ce8d4c6dea760348 Mon Sep 17 00:00:00 2001 From: parthkaria Date: Tue, 29 Aug 2017 17:37:29 +0530 Subject: [PATCH 04/50] Unnecessary Committed --- .../CustomMultiResourcePartitioner.java | 77 -------- .../SpringbatchPartitionConfig.java | 169 ------------------ .../SpringbatchPartitionerApp.java | 28 --- 3 files changed, 274 deletions(-) delete mode 100644 spring-batch/src/main/java/org/baeldung/spring_batch_intro/partitioner/CustomMultiResourcePartitioner.java delete mode 100644 spring-batch/src/main/java/org/baeldung/spring_batch_intro/partitioner/SpringbatchPartitionConfig.java delete mode 100644 spring-batch/src/main/java/org/baeldung/spring_batch_intro/partitioner/SpringbatchPartitionerApp.java diff --git a/spring-batch/src/main/java/org/baeldung/spring_batch_intro/partitioner/CustomMultiResourcePartitioner.java b/spring-batch/src/main/java/org/baeldung/spring_batch_intro/partitioner/CustomMultiResourcePartitioner.java deleted file mode 100644 index 4cae69efbd..0000000000 --- a/spring-batch/src/main/java/org/baeldung/spring_batch_intro/partitioner/CustomMultiResourcePartitioner.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2006-2013 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.baeldung.spring_batch_intro.partitioner; - -import java.util.HashMap; -import java.util.Map; - -import org.springframework.batch.core.partition.support.Partitioner; -import org.springframework.batch.item.ExecutionContext; -import org.springframework.core.io.Resource; -import org.springframework.util.Assert; - -public class CustomMultiResourcePartitioner implements Partitioner { - - private static final String DEFAULT_KEY_NAME = "fileName"; - - private static final String PARTITION_KEY = "partition"; - - private Resource[] resources = new Resource[0]; - - private String keyName = DEFAULT_KEY_NAME; - - /** - * The resources to assign to each partition. In Spring configuration you - * can use a pattern to select multiple resources. - * @param resources the resources to use - */ - public void setResources(Resource[] resources) { - this.resources = resources; - } - - /** - * The name of the key for the file name in each {@link ExecutionContext}. - * Defaults to "fileName". - * @param keyName the value of the key - */ - public void setKeyName(String keyName) { - this.keyName = keyName; - } - - /** - * Assign the filename of each of the injected resources to an - * {@link ExecutionContext}. - * - * @see Partitioner#partition(int) - */ - @Override - public Map partition(int gridSize) { - Map map = new HashMap(gridSize); - int i = 0, k = 1; - for (Resource resource : resources) { - ExecutionContext context = new ExecutionContext(); - Assert.state(resource.exists(), "Resource does not exist: " + resource); - context.putString(keyName, resource.getFilename()); - context.putString("opFileName", "output" + k++ + ".xml"); - - map.put(PARTITION_KEY + i, context); - i++; - } - return map; - } - -} diff --git a/spring-batch/src/main/java/org/baeldung/spring_batch_intro/partitioner/SpringbatchPartitionConfig.java b/spring-batch/src/main/java/org/baeldung/spring_batch_intro/partitioner/SpringbatchPartitionConfig.java deleted file mode 100644 index fe8339a8b4..0000000000 --- a/spring-batch/src/main/java/org/baeldung/spring_batch_intro/partitioner/SpringbatchPartitionConfig.java +++ /dev/null @@ -1,169 +0,0 @@ -package org.baeldung.spring_batch_intro.partitioner; - -import java.io.IOException; -import java.net.MalformedURLException; -import java.text.ParseException; - -import javax.sql.DataSource; - -import org.baeldung.spring_batch_intro.model.Transaction; -import org.baeldung.spring_batch_intro.service.RecordFieldSetMapper; -import org.springframework.batch.core.Job; -import org.springframework.batch.core.Step; -import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; -import org.springframework.batch.core.configuration.annotation.JobBuilderFactory; -import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; -import org.springframework.batch.core.configuration.annotation.StepScope; -import org.springframework.batch.core.launch.JobLauncher; -import org.springframework.batch.core.launch.support.SimpleJobLauncher; -import org.springframework.batch.core.repository.JobRepository; -import org.springframework.batch.core.repository.support.JobRepositoryFactoryBean; -import org.springframework.batch.item.UnexpectedInputException; -import org.springframework.batch.item.file.FlatFileItemReader; -import org.springframework.batch.item.file.mapping.DefaultLineMapper; -import org.springframework.batch.item.file.transform.DelimitedLineTokenizer; -import org.springframework.batch.item.xml.StaxEventItemWriter; -import org.springframework.batch.support.transaction.ResourcelessTransactionManager; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.FileSystemResource; -import org.springframework.core.io.Resource; -import org.springframework.core.io.support.ResourcePatternResolver; -import org.springframework.core.task.TaskExecutor; -import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; -import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; -import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; -import org.springframework.oxm.Marshaller; -import org.springframework.oxm.jaxb.Jaxb2Marshaller; -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; -import org.springframework.transaction.PlatformTransactionManager; - -@Configuration -@EnableBatchProcessing -public class SpringbatchPartitionConfig { - - @Autowired - ResourcePatternResolver resoursePatternResolver; - - @Autowired - private JobBuilderFactory jobs; - - @Autowired - private StepBuilderFactory steps; - - @Bean(name = "partitionerJob") - public Job partitionerJob() throws UnexpectedInputException, MalformedURLException, ParseException { - return jobs.get("partitionerJob") - .start(partitionStep()) - .build(); - } - - @Bean - public Step partitionStep() throws UnexpectedInputException, MalformedURLException, ParseException { - return steps.get("partitionStep") - .partitioner("slaveStep", partitioner()) - .step(slaveStep()) - .taskExecutor(taskExecutor()) - .build(); - } - - @Bean - public Step slaveStep() throws UnexpectedInputException, MalformedURLException, ParseException { - return steps.get("slaveStep") - . chunk(1) - .reader(itemReader(null)) - .writer(itemWriter(marshaller(), null)) - .build(); - } - - @Bean - public CustomMultiResourcePartitioner partitioner() { - CustomMultiResourcePartitioner partitioner = new CustomMultiResourcePartitioner(); - Resource[] resources; - try { - resources = resoursePatternResolver.getResources("file:src/main/resources/input/partitioner/*.csv"); - } catch (IOException e) { - throw new RuntimeException("I/O problems when resolving the input file pattern.", e); - } - partitioner.setResources(resources); - return partitioner; - } - - @Bean - @StepScope - public FlatFileItemReader itemReader(@Value("#{stepExecutionContext[fileName]}") String filename) throws UnexpectedInputException, ParseException { - FlatFileItemReader reader = new FlatFileItemReader(); - DelimitedLineTokenizer tokenizer = new DelimitedLineTokenizer(); - String[] tokens = { "username", "userid", "transactiondate", "amount" }; - tokenizer.setNames(tokens); - reader.setResource(new ClassPathResource("input/partitioner/" + filename)); - DefaultLineMapper lineMapper = new DefaultLineMapper(); - lineMapper.setLineTokenizer(tokenizer); - lineMapper.setFieldSetMapper(new RecordFieldSetMapper()); - reader.setLinesToSkip(1); - reader.setLineMapper(lineMapper); - return reader; - } - - @Bean(destroyMethod = "") - @StepScope - public StaxEventItemWriter itemWriter(Marshaller marshaller, @Value("#{stepExecutionContext[opFileName]}") String filename) throws MalformedURLException { - StaxEventItemWriter itemWriter = new StaxEventItemWriter<>(); - itemWriter.setMarshaller(marshaller); - itemWriter.setRootTagName("transactionRecord"); - itemWriter.setResource(new FileSystemResource("src/main/resources/output/" + filename)); - return itemWriter; - } - - @Bean - public Marshaller marshaller() { - Jaxb2Marshaller marshaller = new Jaxb2Marshaller(); - marshaller.setClassesToBeBound(new Class[] { Transaction.class }); - return marshaller; - } - - @Bean - public TaskExecutor taskExecutor() { - ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); - taskExecutor.setMaxPoolSize(5); - taskExecutor.setCorePoolSize(5); - taskExecutor.setQueueCapacity(5); - taskExecutor.afterPropertiesSet(); - return taskExecutor; - } - - private JobRepository getJobRepository() throws Exception { - JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean(); - factory.setDataSource(dataSource()); - factory.setTransactionManager(getTransactionManager()); - // JobRepositoryFactoryBean's methods Throws Generic Exception, - // it would have been better to have a specific one - factory.afterPropertiesSet(); - return (JobRepository) factory.getObject(); - } - - private DataSource dataSource() { - EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); - EmbeddedDatabase db = builder.setType(EmbeddedDatabaseType.HSQL) - .addScript("classpath:org/springframework/batch/core/schema-drop-h2.sql") - .addScript("classpath:org/springframework/batch/core/schema-h2.sql") - .build(); - return db; - } - - private PlatformTransactionManager getTransactionManager() { - return new ResourcelessTransactionManager(); - } - - public JobLauncher getJobLauncher() throws Exception { - SimpleJobLauncher jobLauncher = new SimpleJobLauncher(); - // SimpleJobLauncher's methods Throws Generic Exception, - // it would have been better to have a specific one - jobLauncher.setJobRepository(getJobRepository()); - jobLauncher.afterPropertiesSet(); - return jobLauncher; - } -} diff --git a/spring-batch/src/main/java/org/baeldung/spring_batch_intro/partitioner/SpringbatchPartitionerApp.java b/spring-batch/src/main/java/org/baeldung/spring_batch_intro/partitioner/SpringbatchPartitionerApp.java deleted file mode 100644 index 1d6d922969..0000000000 --- a/spring-batch/src/main/java/org/baeldung/spring_batch_intro/partitioner/SpringbatchPartitionerApp.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.baeldung.spring_batch_intro.partitioner; - -import org.springframework.batch.core.Job; -import org.springframework.batch.core.JobExecution; -import org.springframework.batch.core.JobParameters; -import org.springframework.batch.core.launch.JobLauncher; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; - -public class SpringbatchPartitionerApp { - public static void main(final String[] args) { - // Spring Java config - final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); - context.register(SpringbatchPartitionConfig.class); - context.refresh(); - - final JobLauncher jobLauncher = (JobLauncher) context.getBean("jobLauncher"); - final Job job = (Job) context.getBean("partitionerJob"); - System.out.println("Starting the batch job"); - try { - final JobExecution execution = jobLauncher.run(job, new JobParameters()); - System.out.println("Job Status : " + execution.getStatus()); - System.out.println("Job succeeded"); - } catch (final Exception e) { - e.printStackTrace(); - System.out.println("Job failed"); - } - } -} \ No newline at end of file From f6216f27549b70ea891582cab92311b554b0697d Mon Sep 17 00:00:00 2001 From: parthkaria Date: Thu, 31 Aug 2017 12:55:56 +0530 Subject: [PATCH 05/50] BAEL-1106 Move code to libraries from core-java --- core-java/pom.xml | 7 ------- libraries/pom.xml | 6 ++++++ .../main/java/com/baeldung/javax/measure/WaterTank.java | 0 .../java/com/baeldung/javax/measure/WaterTankTests.java | 0 4 files changed, 6 insertions(+), 7 deletions(-) rename {core-java => libraries}/src/main/java/com/baeldung/javax/measure/WaterTank.java (100%) rename {core-java => libraries}/src/test/java/com/baeldung/javax/measure/WaterTankTests.java (100%) diff --git a/core-java/pom.xml b/core-java/pom.xml index 5410ea54e2..c532d80bd6 100644 --- a/core-java/pom.xml +++ b/core-java/pom.xml @@ -213,12 +213,6 @@ ${streamex.version} - - javax.measure - jsr-275 - ${javax-measure.version} - - @@ -445,7 +439,6 @@ 1.5.1 1.13 0.6.5 - 0.9.1 1.3 diff --git a/libraries/pom.xml b/libraries/pom.xml index cf16f0fb79..9b8f3f0a77 100644 --- a/libraries/pom.xml +++ b/libraries/pom.xml @@ -498,6 +498,11 @@ gt-swing ${geotools.version} + + javax.measure + jsr-275 + ${javax-measure.version} + @@ -564,5 +569,6 @@ 0.6.5 0.9.0 15.2 + 0.9.1 diff --git a/core-java/src/main/java/com/baeldung/javax/measure/WaterTank.java b/libraries/src/main/java/com/baeldung/javax/measure/WaterTank.java similarity index 100% rename from core-java/src/main/java/com/baeldung/javax/measure/WaterTank.java rename to libraries/src/main/java/com/baeldung/javax/measure/WaterTank.java diff --git a/core-java/src/test/java/com/baeldung/javax/measure/WaterTankTests.java b/libraries/src/test/java/com/baeldung/javax/measure/WaterTankTests.java similarity index 100% rename from core-java/src/test/java/com/baeldung/javax/measure/WaterTankTests.java rename to libraries/src/test/java/com/baeldung/javax/measure/WaterTankTests.java From 99e9e2332b9d46627836b85a1ed484fec3a53120 Mon Sep 17 00:00:00 2001 From: parthkaria Date: Thu, 31 Aug 2017 12:57:54 +0530 Subject: [PATCH 06/50] BAEL-1106 Move code to libraries from core-java --- core-java/pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/core-java/pom.xml b/core-java/pom.xml index c532d80bd6..0f8a665fa1 100644 --- a/core-java/pom.xml +++ b/core-java/pom.xml @@ -212,7 +212,6 @@ streamex ${streamex.version} - From 119c288942d7f3547515ec773f1e106c6cb50676 Mon Sep 17 00:00:00 2001 From: Jose Carvajal Date: Fri, 8 Sep 2017 11:09:04 +0200 Subject: [PATCH 07/50] BAEL-1069: Guide to diamond operator in Java --- .../java/org/baeldung/java/diamond/Car.java | 5 +++++ .../java/diamond/DiamondOperatorTest.java | 13 +++++++++++++ .../java/org/baeldung/java/diamond/Diesel.java | 10 ++++++++++ .../java/org/baeldung/java/diamond/Engine.java | 6 ++++++ .../java/org/baeldung/java/diamond/Vehicle.java | 5 +++++ .../baeldung/java/rawtypes/RawTypesTest.java | 17 +++++++++++++++++ 6 files changed, 56 insertions(+) create mode 100644 core-java/src/test/java/org/baeldung/java/diamond/Car.java create mode 100644 core-java/src/test/java/org/baeldung/java/diamond/DiamondOperatorTest.java create mode 100644 core-java/src/test/java/org/baeldung/java/diamond/Diesel.java create mode 100644 core-java/src/test/java/org/baeldung/java/diamond/Engine.java create mode 100644 core-java/src/test/java/org/baeldung/java/diamond/Vehicle.java create mode 100644 core-java/src/test/java/org/baeldung/java/rawtypes/RawTypesTest.java diff --git a/core-java/src/test/java/org/baeldung/java/diamond/Car.java b/core-java/src/test/java/org/baeldung/java/diamond/Car.java new file mode 100644 index 0000000000..9f923e0f3b --- /dev/null +++ b/core-java/src/test/java/org/baeldung/java/diamond/Car.java @@ -0,0 +1,5 @@ +package org.baeldung.java.diamond; + +public class Car implements Vehicle { + +} diff --git a/core-java/src/test/java/org/baeldung/java/diamond/DiamondOperatorTest.java b/core-java/src/test/java/org/baeldung/java/diamond/DiamondOperatorTest.java new file mode 100644 index 0000000000..ceb7c87100 --- /dev/null +++ b/core-java/src/test/java/org/baeldung/java/diamond/DiamondOperatorTest.java @@ -0,0 +1,13 @@ +package org.baeldung.java.diamond; + +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; + +public class DiamondOperatorTest { + @Test + public void shouldCreateCarUsingDiamondWithoutWarnings() { + Car myCar = new Car<>(); + assertNotNull(myCar); + } +} diff --git a/core-java/src/test/java/org/baeldung/java/diamond/Diesel.java b/core-java/src/test/java/org/baeldung/java/diamond/Diesel.java new file mode 100644 index 0000000000..dc4256cdae --- /dev/null +++ b/core-java/src/test/java/org/baeldung/java/diamond/Diesel.java @@ -0,0 +1,10 @@ +package org.baeldung.java.diamond; + +public class Diesel implements Engine { + + @Override + public void start() { + System.out.println("Started Diesel..."); + } + +} diff --git a/core-java/src/test/java/org/baeldung/java/diamond/Engine.java b/core-java/src/test/java/org/baeldung/java/diamond/Engine.java new file mode 100644 index 0000000000..c18a8f64b5 --- /dev/null +++ b/core-java/src/test/java/org/baeldung/java/diamond/Engine.java @@ -0,0 +1,6 @@ +package org.baeldung.java.diamond; + +public interface Engine { + + void start(); +} diff --git a/core-java/src/test/java/org/baeldung/java/diamond/Vehicle.java b/core-java/src/test/java/org/baeldung/java/diamond/Vehicle.java new file mode 100644 index 0000000000..f61cf59620 --- /dev/null +++ b/core-java/src/test/java/org/baeldung/java/diamond/Vehicle.java @@ -0,0 +1,5 @@ +package org.baeldung.java.diamond; + +public interface Vehicle { + +} diff --git a/core-java/src/test/java/org/baeldung/java/rawtypes/RawTypesTest.java b/core-java/src/test/java/org/baeldung/java/rawtypes/RawTypesTest.java new file mode 100644 index 0000000000..2b36786abf --- /dev/null +++ b/core-java/src/test/java/org/baeldung/java/rawtypes/RawTypesTest.java @@ -0,0 +1,17 @@ +package org.baeldung.java.rawtypes; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +public class RawTypesTest { + @Test + public void shouldCreateListUsingRawTypes() { + @SuppressWarnings("rawtypes") + List myList = new ArrayList(); + myList.add(new Object()); + myList.add("2"); + myList.add(new Integer(1)); + } +} From 239da95f3d21ec25e945bda22d7e176bc3998bd6 Mon Sep 17 00:00:00 2001 From: Jose Carvajal Date: Wed, 13 Sep 2017 14:36:14 +0200 Subject: [PATCH 08/50] Changes after review --- ...{DiamondOperatorTest.java => DiamondOperatorUnitTest.java} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename core-java/src/test/java/org/baeldung/java/diamond/{DiamondOperatorTest.java => DiamondOperatorUnitTest.java} (65%) diff --git a/core-java/src/test/java/org/baeldung/java/diamond/DiamondOperatorTest.java b/core-java/src/test/java/org/baeldung/java/diamond/DiamondOperatorUnitTest.java similarity index 65% rename from core-java/src/test/java/org/baeldung/java/diamond/DiamondOperatorTest.java rename to core-java/src/test/java/org/baeldung/java/diamond/DiamondOperatorUnitTest.java index ceb7c87100..f6c7f7162f 100644 --- a/core-java/src/test/java/org/baeldung/java/diamond/DiamondOperatorTest.java +++ b/core-java/src/test/java/org/baeldung/java/diamond/DiamondOperatorUnitTest.java @@ -4,9 +4,9 @@ import static org.junit.Assert.assertNotNull; import org.junit.Test; -public class DiamondOperatorTest { +public class DiamondOperatorUnitTest { @Test - public void shouldCreateCarUsingDiamondWithoutWarnings() { + public void whenCreateCarUsingDiamondOperator_thenSuccess() { Car myCar = new Car<>(); assertNotNull(myCar); } From f52560cdbbd85206d975756526bedb9e59c76eb4 Mon Sep 17 00:00:00 2001 From: parthkaria Date: Wed, 27 Sep 2017 14:51:43 +0530 Subject: [PATCH 09/50] BAEL-815 Introduction to JGraphT --- libraries/pom.xml | 5 + .../com/baeldung/javax/measure/WaterTank.java | 26 ----- .../javax/measure/WaterTankTests.java | 68 ------------- .../baeldung/jgrapht/CompleteGraphTest.java | 38 ++++++++ .../baeldung/jgrapht/DirectedGraphTests.java | 95 +++++++++++++++++++ .../baeldung/jgrapht/EulerianCircuitTest.java | 42 ++++++++ 6 files changed, 180 insertions(+), 94 deletions(-) delete mode 100644 libraries/src/main/java/com/baeldung/javax/measure/WaterTank.java delete mode 100644 libraries/src/test/java/com/baeldung/javax/measure/WaterTankTests.java create mode 100644 libraries/src/test/java/com/baeldung/jgrapht/CompleteGraphTest.java create mode 100644 libraries/src/test/java/com/baeldung/jgrapht/DirectedGraphTests.java create mode 100644 libraries/src/test/java/com/baeldung/jgrapht/EulerianCircuitTest.java diff --git a/libraries/pom.xml b/libraries/pom.xml index b0fdbea2f6..a15ffa6dfc 100644 --- a/libraries/pom.xml +++ b/libraries/pom.xml @@ -590,6 +590,11 @@ jsr-275 ${javax-measure.version} + + org.jgrapht + jgrapht-core + 1.0.1 + diff --git a/libraries/src/main/java/com/baeldung/javax/measure/WaterTank.java b/libraries/src/main/java/com/baeldung/javax/measure/WaterTank.java deleted file mode 100644 index 1ab9eee53f..0000000000 --- a/libraries/src/main/java/com/baeldung/javax/measure/WaterTank.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.baeldung.javax.measure; - -import javax.measure.Measure; -import javax.measure.quantity.Volume; - -public class WaterTank { - - private Measure capacityMeasure; - private double capacityDouble; - - public void setCapacityMeasure(Measure capacityMeasure) { - this.capacityMeasure = capacityMeasure; - } - - public void setCapacityDouble(double capacityDouble) { - this.capacityDouble = capacityDouble; - } - - public Measure getCapacityMeasure() { - return capacityMeasure; - } - - public double getCapacityDouble() { - return capacityDouble; - } -} diff --git a/libraries/src/test/java/com/baeldung/javax/measure/WaterTankTests.java b/libraries/src/test/java/com/baeldung/javax/measure/WaterTankTests.java deleted file mode 100644 index ef54035353..0000000000 --- a/libraries/src/test/java/com/baeldung/javax/measure/WaterTankTests.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.baeldung.javax.measure; - -import javax.measure.Measure; -import javax.measure.converter.UnitConverter; -import javax.measure.quantity.Duration; -import javax.measure.quantity.Length; -import javax.measure.quantity.Pressure; -import javax.measure.quantity.Volume; -import static javax.measure.unit.NonSI.HOUR; -import static javax.measure.unit.NonSI.LITRE; -import static javax.measure.unit.NonSI.MILE; -import static javax.measure.unit.NonSI.MINUTE; -import javax.measure.unit.SI; -import static javax.measure.unit.SI.KILO; -import static javax.measure.unit.SI.METER; -import static javax.measure.unit.SI.NEWTON; -import static javax.measure.unit.SI.SECOND; -import javax.measure.unit.Unit; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import org.junit.Test; - -public class WaterTankTests { - - @Test - public void givenMeasure_whenGetUnitAndConvertValue_thenSuccess() { - WaterTank waterTank = new WaterTank(); - waterTank.setCapacityMeasure(Measure.valueOf(9.2, LITRE)); - assertEquals(LITRE, waterTank.getCapacityMeasure().getUnit()); - - Measure waterCapacity = waterTank.getCapacityMeasure(); - double volumeInLitre = waterCapacity.getValue().doubleValue(); - assertEquals(9.2, volumeInLitre, 0.0f); - - double volumeInMilliLitre = waterCapacity.doubleValue(SI.MILLI(LITRE)); - assertEquals(9200.0, volumeInMilliLitre, 0.0f); - - Unit Kilometer = SI.KILO(METER); - Unit Centimeter = SI.CENTI(METER); - } - - @Test - public void givenMeasure_whenAlternateMeasure_ThenGetAlternateMeasure() { - Unit PASCAL = NEWTON.divide(METER.pow(2)).alternate("Pa"); - assertTrue(Unit.valueOf("Pa").equals(PASCAL)); - } - - @Test - public void givenMeasure_whenCompoundMeasure_ThenGetCompoundMeasure() { - Unit HOUR_MINUTE_SECOND = HOUR.compound(MINUTE).compound(SECOND); - Measure duration = Measure.valueOf(12345, SECOND); - assertEquals("3h25min45s", duration.to(HOUR_MINUTE_SECOND).toString()); - } - - @Test - public void givenMiles_whenConvertToKilometer_ThenConverted() { - double distanceInMiles = 50.0; - UnitConverter mileToKilometer = MILE.getConverterTo(KILO(METER)); - double distanceInKilometers = mileToKilometer.convert(distanceInMiles); - assertEquals(80.4672, distanceInKilometers, 0.00f); - } - - @Test - public void givenSymbol_WhenCompareToSystemUnit_ThenSuccess() { - assertTrue(Unit.valueOf("kW").equals(SI.KILO(SI.WATT))); - assertTrue(Unit.valueOf("ms").equals(SI.SECOND.divide(1000))); - } -} diff --git a/libraries/src/test/java/com/baeldung/jgrapht/CompleteGraphTest.java b/libraries/src/test/java/com/baeldung/jgrapht/CompleteGraphTest.java new file mode 100644 index 0000000000..c085d54689 --- /dev/null +++ b/libraries/src/test/java/com/baeldung/jgrapht/CompleteGraphTest.java @@ -0,0 +1,38 @@ +package com.baeldung.jgrapht; + +import static org.junit.Assert.assertEquals; + +import java.util.List; + +import org.jgrapht.VertexFactory; +import org.jgrapht.alg.HamiltonianCycle; +import org.jgrapht.generate.CompleteGraphGenerator; +import org.jgrapht.graph.DefaultEdge; +import org.jgrapht.graph.SimpleWeightedGraph; +import org.junit.Before; +import org.junit.Test; + +public class CompleteGraphTest { + + static SimpleWeightedGraph completeGraph; + static int size = 10; + + @Before + public void createCompleteGraph() { + completeGraph = new SimpleWeightedGraph<>(DefaultEdge.class); + CompleteGraphGenerator completeGenerator = new CompleteGraphGenerator(size); + VertexFactory vFactory = new VertexFactory() { + private int id = 0; + public String createVertex() { + return "v" + id++; + } + }; + completeGenerator.generateGraph(completeGraph, vFactory, null); + } + + @Test + public void givenCompleteGraph_whenGetHamiltonianCyclePath_thenGetVerticeListInSequence() { + List verticeList = HamiltonianCycle.getApproximateOptimalForCompleteGraph(completeGraph); + assertEquals(verticeList.size(), completeGraph.vertexSet().size()); + } +} diff --git a/libraries/src/test/java/com/baeldung/jgrapht/DirectedGraphTests.java b/libraries/src/test/java/com/baeldung/jgrapht/DirectedGraphTests.java new file mode 100644 index 0000000000..7f4cc99715 --- /dev/null +++ b/libraries/src/test/java/com/baeldung/jgrapht/DirectedGraphTests.java @@ -0,0 +1,95 @@ +package com.baeldung.jgrapht; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.stream.IntStream; + +import org.jgrapht.DirectedGraph; +import org.jgrapht.GraphPath; +import org.jgrapht.alg.CycleDetector; +import org.jgrapht.alg.KosarajuStrongConnectivityInspector; +import org.jgrapht.alg.interfaces.StrongConnectivityAlgorithm; +import org.jgrapht.alg.shortestpath.AllDirectedPaths; +import org.jgrapht.alg.shortestpath.BellmanFordShortestPath; +import org.jgrapht.alg.shortestpath.DijkstraShortestPath; +import org.jgrapht.graph.DefaultDirectedGraph; +import org.jgrapht.graph.DefaultEdge; +import org.jgrapht.graph.DirectedSubgraph; +import org.jgrapht.traverse.BreadthFirstIterator; +import org.jgrapht.traverse.DepthFirstIterator; +import org.junit.Before; +import org.junit.Test; + +public class DirectedGraphTests { + DirectedGraph directedGraph; + + @Before + public void createDirectedGraph() { + directedGraph = new DefaultDirectedGraph(DefaultEdge.class); + IntStream.range(1, 10).forEach(i -> { + directedGraph.addVertex("v" + i); + }); + directedGraph.addEdge("v1", "v2"); + directedGraph.addEdge("v2", "v4"); + directedGraph.addEdge("v4", "v3"); + directedGraph.addEdge("v3", "v1"); + directedGraph.addEdge("v5", "v4"); + directedGraph.addEdge("v5", "v6"); + directedGraph.addEdge("v6", "v7"); + directedGraph.addEdge("v7", "v5"); + directedGraph.addEdge("v8", "v5"); + directedGraph.addEdge("v9", "v8"); + } + + @Test + public void givenDirectedGraph_whenGetStronglyConnectedSubgraphs_thenPathExistsBetweenStronglyconnectedVertices() { + StrongConnectivityAlgorithm scAlg = new KosarajuStrongConnectivityInspector<>(directedGraph); + List> stronglyConnectedSubgraphs = scAlg.stronglyConnectedSubgraphs(); + List stronglyConnectedVertices = new ArrayList<>(stronglyConnectedSubgraphs.get(3).vertexSet()); + + String randomVertex1 = stronglyConnectedVertices.get(0); + String randomVertex2 = stronglyConnectedVertices.get(3); + AllDirectedPaths allDirectedPaths = new AllDirectedPaths<>(directedGraph); + + List> possiblePathList = allDirectedPaths.getAllPaths(randomVertex1, randomVertex2, false, stronglyConnectedVertices.size()); + assertTrue(possiblePathList.size() > 0); + } + + @Test + public void givenDirectedGraphWithCycle_whenCheckCycles_thenDetectCycles() { + CycleDetector cycleDetector = new CycleDetector(directedGraph); + assertTrue(cycleDetector.detectCycles()); + Set cycleVertices = cycleDetector.findCycles(); + assertTrue(cycleVertices.size() > 0); + } + + @Test + public void givenDirectedGraph_whenCreateInstanceDepthFirstIterator_thenGetIterator() { + DepthFirstIterator depthFirstIterator = new DepthFirstIterator<>(directedGraph); + assertNotNull(depthFirstIterator); + } + + @Test + public void givenDirectedGraph_whenCreateInstanceBreadthFirstIterator_thenGetIterator() { + BreadthFirstIterator breadthFirstIterator = new BreadthFirstIterator<>(directedGraph); + assertNotNull(breadthFirstIterator); + } + + @Test + public void givenDirectedGraph_whenGetDijkstraShortestPath_thenGetNotNullPath() { + DijkstraShortestPath dijkstraShortestPath = new DijkstraShortestPath(directedGraph); + List shortestPath = dijkstraShortestPath.getPath("v1", "v4").getVertexList(); + assertNotNull(shortestPath); + } + + @Test + public void givenDirectedGraph_whenGetBellmanFordShortestPath_thenGetNotNullPath() { + BellmanFordShortestPath bellmanFordShortestPath = new BellmanFordShortestPath(directedGraph); + List shortestPath = bellmanFordShortestPath.getPath("v1", "v4").getVertexList(); + assertNotNull(shortestPath); + } +} diff --git a/libraries/src/test/java/com/baeldung/jgrapht/EulerianCircuitTest.java b/libraries/src/test/java/com/baeldung/jgrapht/EulerianCircuitTest.java new file mode 100644 index 0000000000..6f0fb92ab7 --- /dev/null +++ b/libraries/src/test/java/com/baeldung/jgrapht/EulerianCircuitTest.java @@ -0,0 +1,42 @@ +package com.baeldung.jgrapht; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.stream.IntStream; + +import org.jgrapht.GraphPath; +import org.jgrapht.alg.cycle.HierholzerEulerianCycle; +import org.jgrapht.graph.DefaultEdge; +import org.jgrapht.graph.SimpleWeightedGraph; +import org.junit.Before; +import org.junit.Test; + +public class EulerianCircuitTest { + SimpleWeightedGraph simpleGraph; + + @Before + public void createGraphWithEulerianCircuit() { + simpleGraph = new SimpleWeightedGraph<>(DefaultEdge.class); + IntStream.range(1, 6).forEach(i -> { + simpleGraph.addVertex("v" + i); + }); + IntStream.range(1, 6).forEach(i -> { + int endVertexNo = (i + 1) > 5 ? 1 : i + 1; + simpleGraph.addEdge("v" + i, "v" + endVertexNo); + }); + } + + @Test + public void givenGraph_whenCheckEluerianCycle_thenGetResult() { + HierholzerEulerianCycle eulerianCycle = new HierholzerEulerianCycle<>(); + assertTrue(eulerianCycle.isEulerian(simpleGraph)); + } + + @Test + public void givenGraphWithEulerianCircuit_whenGetEulerianCycle_thenGetGraphPath() { + HierholzerEulerianCycle eulerianCycle = new HierholzerEulerianCycle<>(); + GraphPath path = eulerianCycle.getEulerianCycle(simpleGraph); + assertTrue(path.getEdgeList().containsAll(simpleGraph.edgeSet())); + } +} From 38b2dc00284a18eda549c50daa47f85bfa128183 Mon Sep 17 00:00:00 2001 From: parthkaria Date: Wed, 27 Sep 2017 14:52:54 +0530 Subject: [PATCH 10/50] BAEL-815 Introduction to JGraphT --- libraries/pom.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/libraries/pom.xml b/libraries/pom.xml index a15ffa6dfc..03919b9cec 100644 --- a/libraries/pom.xml +++ b/libraries/pom.xml @@ -585,11 +585,6 @@ fugue 3.0.0-m007 - - javax.measure - jsr-275 - ${javax-measure.version} - org.jgrapht jgrapht-core @@ -660,7 +655,6 @@ 0.6.5 0.9.0 15.2 - 0.9.1 2.9.9 1.5.1 2.3.0 From 57a993de64fc0959b0511096f07ac9d59133fa01 Mon Sep 17 00:00:00 2001 From: Jose Carvajal Date: Sun, 8 Oct 2017 08:17:24 +0200 Subject: [PATCH 11/50] BAEL-719: Intro to Spring Cloud Stream --- spring-cloud/pom.xml | 2 + spring-cloud/spring-cloud-stream/pom.xml | 70 +++++++++++++++++++ .../spring-cloud-stream-rabbit/pom.xml | 33 +++++++++ .../MultipleOutputsServiceApplication.java | 38 ++++++++++ ...tputsWithConditionsServiceApplication.java | 39 +++++++++++ .../rabbit/MyLoggerServiceApplication.java | 32 +++++++++ .../messages/TextPlainMessageConverter.java | 26 +++++++ .../cloud/stream/rabbit/model/LogMessage.java | 32 +++++++++ .../stream/rabbit/processor/MyProcessor.java | 19 +++++ .../src/main/resources/application.yml | 28 ++++++++ ...ultipleOutputsServiceApplicationTests.java | 52 ++++++++++++++ ...WithConditionsServiceApplicationTests.java | 52 ++++++++++++++ .../rabbit/MyLoggerApplicationTests.java | 44 ++++++++++++ 13 files changed, 467 insertions(+) create mode 100644 spring-cloud/spring-cloud-stream/pom.xml create mode 100644 spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/pom.xml create mode 100644 spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/main/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsServiceApplication.java create mode 100644 spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/main/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsWithConditionsServiceApplication.java create mode 100644 spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/main/java/com/baeldung/spring/cloud/stream/rabbit/MyLoggerServiceApplication.java create mode 100644 spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/main/java/com/baeldung/spring/cloud/stream/rabbit/messages/TextPlainMessageConverter.java create mode 100644 spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/main/java/com/baeldung/spring/cloud/stream/rabbit/model/LogMessage.java create mode 100644 spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/main/java/com/baeldung/spring/cloud/stream/rabbit/processor/MyProcessor.java create mode 100644 spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/main/resources/application.yml create mode 100644 spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsServiceApplicationTests.java create mode 100644 spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsWithConditionsServiceApplicationTests.java create mode 100644 spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MyLoggerApplicationTests.java diff --git a/spring-cloud/pom.xml b/spring-cloud/pom.xml index 44e72535f8..f3083f69b7 100644 --- a/spring-cloud/pom.xml +++ b/spring-cloud/pom.xml @@ -16,6 +16,7 @@ spring-cloud-rest spring-cloud-zookeeper spring-cloud-gateway + spring-cloud-stream pom @@ -37,6 +38,7 @@ 1.2.3.RELEASE 1.2.3.RELEASE 1.2.3.RELEASE + 1.3.0.RELEASE 1.4.2.RELEASE 3.6.0 1.4.2.RELEASE diff --git a/spring-cloud/spring-cloud-stream/pom.xml b/spring-cloud/spring-cloud-stream/pom.xml new file mode 100644 index 0000000000..352b52cc17 --- /dev/null +++ b/spring-cloud/spring-cloud-stream/pom.xml @@ -0,0 +1,70 @@ + + + 4.0.0 + + org.baeldung + spring-cloud-stream + + spring-cloud-stream-rabbit + + pom + + spring-cloud-stream + + + com.baeldung.spring.cloud + spring-cloud + 1.0.0-SNAPSHOT + + + + UTF-8 + 3.6.0 + + + + + + org.springframework.cloud + spring-cloud-starter-stream-rabbit + ${spring-cloud-stream.version} + + + + org.springframework.cloud + spring-cloud-stream + ${spring-cloud-stream.version} + + + + org.springframework.cloud + spring-cloud-stream-test-support + ${spring-cloud-stream.version} + test + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + 1.8 + 1.8 + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot-maven-plugin.version} + + + + + diff --git a/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/pom.xml b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/pom.xml new file mode 100644 index 0000000000..9eede9e92c --- /dev/null +++ b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/pom.xml @@ -0,0 +1,33 @@ + + + 4.0.0 + + spring-cloud-stream-rabbit + jar + + spring-cloud-stream-rabbit + Simple Spring Cloud Stream + + + org.baeldung + spring-cloud-stream + 1.0.0-SNAPSHOT + .. + + + + + + org.springframework.cloud + spring-cloud-starter-stream-rabbit + + + + org.springframework.cloud + spring-cloud-stream-test-support + test + + + + diff --git a/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/main/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsServiceApplication.java b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/main/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsServiceApplication.java new file mode 100644 index 0000000000..375494dfac --- /dev/null +++ b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/main/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsServiceApplication.java @@ -0,0 +1,38 @@ +package com.baeldung.spring.cloud.stream.rabbit; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.stream.annotation.EnableBinding; +import org.springframework.cloud.stream.annotation.StreamListener; +import org.springframework.messaging.Message; +import org.springframework.messaging.support.MessageBuilder; + +import com.baeldung.spring.cloud.stream.rabbit.processor.MyProcessor; + +@SpringBootApplication +@EnableBinding(MyProcessor.class) +public class MultipleOutputsServiceApplication { + public static void main(String[] args) { + SpringApplication.run(MultipleOutputsServiceApplication.class, args); + } + + @Autowired + private MyProcessor processor; + + @StreamListener(MyProcessor.INPUT) + public void routeValues(Integer val) { + if (val < 10) { + processor.anOutput() + .send(message(val)); + } else { + processor.anotherOutput() + .send(message(val)); + } + } + + private static final Message message(T val) { + return MessageBuilder.withPayload(val) + .build(); + } +} diff --git a/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/main/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsWithConditionsServiceApplication.java b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/main/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsWithConditionsServiceApplication.java new file mode 100644 index 0000000000..4729e418b6 --- /dev/null +++ b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/main/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsWithConditionsServiceApplication.java @@ -0,0 +1,39 @@ +package com.baeldung.spring.cloud.stream.rabbit; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.stream.annotation.EnableBinding; +import org.springframework.cloud.stream.annotation.StreamListener; +import org.springframework.messaging.Message; +import org.springframework.messaging.support.MessageBuilder; + +import com.baeldung.spring.cloud.stream.rabbit.processor.MyProcessor; + +@SpringBootApplication +@EnableBinding(MyProcessor.class) +public class MultipleOutputsWithConditionsServiceApplication { + public static void main(String[] args) { + SpringApplication.run(MultipleOutputsWithConditionsServiceApplication.class, args); + } + + @Autowired + private MyProcessor processor; + + @StreamListener(target = MyProcessor.INPUT, condition = "payload < 10") + public void routeValuesToAnOutput(Integer val) { + processor.anOutput() + .send(message(val)); + } + + @StreamListener(target = MyProcessor.INPUT, condition = "payload >= 10") + public void routeValuesToAnotherOutput(Integer val) { + processor.anotherOutput() + .send(message(val)); + } + + private static final Message message(T val) { + return MessageBuilder.withPayload(val) + .build(); + } +} diff --git a/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/main/java/com/baeldung/spring/cloud/stream/rabbit/MyLoggerServiceApplication.java b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/main/java/com/baeldung/spring/cloud/stream/rabbit/MyLoggerServiceApplication.java new file mode 100644 index 0000000000..aac551e544 --- /dev/null +++ b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/main/java/com/baeldung/spring/cloud/stream/rabbit/MyLoggerServiceApplication.java @@ -0,0 +1,32 @@ +package com.baeldung.spring.cloud.stream.rabbit; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.stream.annotation.EnableBinding; +import org.springframework.cloud.stream.annotation.StreamListener; +import org.springframework.cloud.stream.messaging.Processor; +import org.springframework.context.annotation.Bean; +import org.springframework.messaging.converter.MessageConverter; +import org.springframework.messaging.handler.annotation.SendTo; + +import com.baeldung.spring.cloud.stream.rabbit.messages.TextPlainMessageConverter; +import com.baeldung.spring.cloud.stream.rabbit.model.LogMessage; + +@SpringBootApplication +@EnableBinding(Processor.class) +public class MyLoggerServiceApplication { + public static void main(String[] args) { + SpringApplication.run(MyLoggerServiceApplication.class, args); + } + + @Bean + public MessageConverter providesTextPlainMessageConverter() { + return new TextPlainMessageConverter(); + } + + @StreamListener(Processor.INPUT) + @SendTo(Processor.OUTPUT) + public LogMessage enrichLogMessage(LogMessage log) { + return new LogMessage(String.format("[1]: %s", log.getMessage())); + } +} diff --git a/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/main/java/com/baeldung/spring/cloud/stream/rabbit/messages/TextPlainMessageConverter.java b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/main/java/com/baeldung/spring/cloud/stream/rabbit/messages/TextPlainMessageConverter.java new file mode 100644 index 0000000000..d690ee38a9 --- /dev/null +++ b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/main/java/com/baeldung/spring/cloud/stream/rabbit/messages/TextPlainMessageConverter.java @@ -0,0 +1,26 @@ +package com.baeldung.spring.cloud.stream.rabbit.messages; + +import org.springframework.messaging.Message; +import org.springframework.messaging.converter.AbstractMessageConverter; +import org.springframework.util.MimeType; + +import com.baeldung.spring.cloud.stream.rabbit.model.LogMessage; + +public class TextPlainMessageConverter extends AbstractMessageConverter { + + public TextPlainMessageConverter() { + super(new MimeType("text", "plain")); + } + + @Override + protected boolean supports(Class clazz) { + return (LogMessage.class == clazz); + } + + @Override + protected Object convertFromInternal(Message message, Class targetClass, Object conversionHint) { + Object payload = message.getPayload(); + String text = payload instanceof String ? (String) payload : new String((byte[]) payload); + return new LogMessage(text); + } +} diff --git a/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/main/java/com/baeldung/spring/cloud/stream/rabbit/model/LogMessage.java b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/main/java/com/baeldung/spring/cloud/stream/rabbit/model/LogMessage.java new file mode 100644 index 0000000000..44a6ca4d4e --- /dev/null +++ b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/main/java/com/baeldung/spring/cloud/stream/rabbit/model/LogMessage.java @@ -0,0 +1,32 @@ +package com.baeldung.spring.cloud.stream.rabbit.model; + +import java.io.Serializable; + +public class LogMessage implements Serializable { + + private static final long serialVersionUID = -5857383701708275796L; + + private String message; + + public LogMessage() { + + } + + public LogMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + @Override + public String toString() { + return message; + } + +} diff --git a/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/main/java/com/baeldung/spring/cloud/stream/rabbit/processor/MyProcessor.java b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/main/java/com/baeldung/spring/cloud/stream/rabbit/processor/MyProcessor.java new file mode 100644 index 0000000000..563ce06b6f --- /dev/null +++ b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/main/java/com/baeldung/spring/cloud/stream/rabbit/processor/MyProcessor.java @@ -0,0 +1,19 @@ +package com.baeldung.spring.cloud.stream.rabbit.processor; + +import org.springframework.cloud.stream.annotation.Input; +import org.springframework.cloud.stream.annotation.Output; +import org.springframework.messaging.MessageChannel; +import org.springframework.messaging.SubscribableChannel; + +public interface MyProcessor { + String INPUT = "myInput"; + + @Input + SubscribableChannel myInput(); + + @Output("myOutput") + MessageChannel anOutput(); + + @Output + MessageChannel anotherOutput(); +} diff --git a/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/main/resources/application.yml b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/main/resources/application.yml new file mode 100644 index 0000000000..3d9d97a736 --- /dev/null +++ b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/main/resources/application.yml @@ -0,0 +1,28 @@ +spring: + cloud: + stream: + bindings: + input: + destination: queue.log.messages + binder: local_rabbit + group: logMessageConsumers + output: + destination: queue.pretty.log.messages + binder: local_rabbit + binders: + local_rabbit: + type: rabbit + environment: + spring: + rabbitmq: + host: localhost + port: 5672 + username: guest + password: guest + virtual-host: / +server: + port: 0 +management: + health: + binders: + enabled: true \ No newline at end of file diff --git a/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsServiceApplicationTests.java b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsServiceApplicationTests.java new file mode 100644 index 0000000000..18dcd8da7c --- /dev/null +++ b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsServiceApplicationTests.java @@ -0,0 +1,52 @@ +package com.baeldung.spring.cloud.stream.rabbit; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.stream.test.binder.MessageCollector; +import org.springframework.messaging.MessageChannel; +import org.springframework.messaging.support.MessageBuilder; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.baeldung.spring.cloud.stream.rabbit.processor.MyProcessor; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = MultipleOutputsServiceApplication.class) +@DirtiesContext +public class MultipleOutputsServiceApplicationTests { + + @Autowired + private MyProcessor pipe; + + @Autowired + private MessageCollector messageCollector; + + @Test + public void shouldReceiveMessageInAnOutput() { + whenSendMessage(1); + thenPayloadInChannelIs(pipe.anOutput(), 1); + } + + @Test + public void shouldReceiveMessageInAnAnotherOutput() { + whenSendMessage(11); + thenPayloadInChannelIs(pipe.anotherOutput(), 11); + } + + private void whenSendMessage(Integer val) { + pipe.myInput() + .send(MessageBuilder.withPayload(val) + .build()); + } + + private void thenPayloadInChannelIs(MessageChannel channel, Integer expectedValue) { + Object payload = messageCollector.forChannel(channel) + .poll() + .getPayload(); + assertEquals(expectedValue, payload); + } +} diff --git a/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsWithConditionsServiceApplicationTests.java b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsWithConditionsServiceApplicationTests.java new file mode 100644 index 0000000000..02638dea4c --- /dev/null +++ b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsWithConditionsServiceApplicationTests.java @@ -0,0 +1,52 @@ +package com.baeldung.spring.cloud.stream.rabbit; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.stream.test.binder.MessageCollector; +import org.springframework.messaging.MessageChannel; +import org.springframework.messaging.support.MessageBuilder; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.baeldung.spring.cloud.stream.rabbit.processor.MyProcessor; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = MultipleOutputsWithConditionsServiceApplication.class) +@DirtiesContext +public class MultipleOutputsWithConditionsServiceApplicationTests { + + @Autowired + private MyProcessor pipe; + + @Autowired + private MessageCollector messageCollector; + + @Test + public void shouldReceiveMessageInAnOutput() { + whenSendMessage(1); + thenPayloadInChannelIs(pipe.anotherOutput(), 1); + } + + @Test + public void shouldReceiveMessageInAnAnotherOutput() { + whenSendMessage(11); + thenPayloadInChannelIs(pipe.anotherOutput(), 11); + } + + private void whenSendMessage(Integer val) { + pipe.myInput() + .send(MessageBuilder.withPayload(val) + .build()); + } + + private void thenPayloadInChannelIs(MessageChannel channel, Integer expectedValue) { + Object payload = messageCollector.forChannel(channel) + .poll() + .getPayload(); + assertEquals(expectedValue, payload); + } +} diff --git a/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MyLoggerApplicationTests.java b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MyLoggerApplicationTests.java new file mode 100644 index 0000000000..d004987929 --- /dev/null +++ b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MyLoggerApplicationTests.java @@ -0,0 +1,44 @@ +package com.baeldung.spring.cloud.stream.rabbit; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.stream.messaging.Processor; +import org.springframework.cloud.stream.test.binder.MessageCollector; +import org.springframework.messaging.support.MessageBuilder; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.baeldung.spring.cloud.stream.rabbit.MyLoggerServiceApplication; +import com.baeldung.spring.cloud.stream.rabbit.model.LogMessage; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = MyLoggerServiceApplication.class) +@DirtiesContext +public class MyLoggerApplicationTests { + + @Autowired + private Processor pipe; + + @Autowired + private MessageCollector messageCollector; + + @Test + public void shouldEnrichMessage() { + // Send message + pipe.input() + .send(MessageBuilder.withPayload(new LogMessage("This is my message")) + .build()); + + // Get response from the service + Object payload = messageCollector.forChannel(pipe.output()) + .poll() + .getPayload(); + + // Assert + assertEquals("[1]: This is my message", payload.toString()); + } +} From e8979a40336ebacd60aaee3a9363b7ebf7683279 Mon Sep 17 00:00:00 2001 From: Jose Carvajal Date: Wed, 11 Oct 2017 12:11:38 +0200 Subject: [PATCH 12/50] Pull request changes done --- spring-cloud/spring-cloud-stream/pom.xml | 120 +++++++++--------- .../spring-cloud-stream-rabbit/pom.xml | 11 +- ...ultipleOutputsServiceApplicationTests.java | 4 +- ...WithConditionsServiceApplicationTests.java | 4 +- .../rabbit/MyLoggerApplicationTests.java | 6 +- 5 files changed, 70 insertions(+), 75 deletions(-) diff --git a/spring-cloud/spring-cloud-stream/pom.xml b/spring-cloud/spring-cloud-stream/pom.xml index 352b52cc17..2104ba1827 100644 --- a/spring-cloud/spring-cloud-stream/pom.xml +++ b/spring-cloud/spring-cloud-stream/pom.xml @@ -1,70 +1,70 @@ - 4.0.0 + xmlns="http://maven.apache.org/POM/4.0.0" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 - org.baeldung - spring-cloud-stream - - spring-cloud-stream-rabbit - - pom + org.baeldung + spring-cloud-stream + + spring-cloud-stream-rabbit + + pom - spring-cloud-stream + spring-cloud-stream - - com.baeldung.spring.cloud - spring-cloud - 1.0.0-SNAPSHOT - + + com.baeldung.spring.cloud + spring-cloud + 1.0.0-SNAPSHOT + - - UTF-8 - 3.6.0 - + + UTF-8 + 3.6.0 + - - - - org.springframework.cloud - spring-cloud-starter-stream-rabbit - ${spring-cloud-stream.version} - - - - org.springframework.cloud - spring-cloud-stream - ${spring-cloud-stream.version} - + + + + org.springframework.cloud + spring-cloud-starter-stream-rabbit + ${spring-cloud-stream.version} + + + + org.springframework.cloud + spring-cloud-stream + ${spring-cloud-stream.version} + - - org.springframework.cloud - spring-cloud-stream-test-support - ${spring-cloud-stream.version} - test - - - + + org.springframework.cloud + spring-cloud-stream-test-support + ${spring-cloud-stream.version} + test + + + - - - - - org.apache.maven.plugins - maven-compiler-plugin - ${maven-compiler-plugin.version} - - 1.8 - 1.8 - - - - org.springframework.boot - spring-boot-maven-plugin - ${spring-boot-maven-plugin.version} - - - - + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + 1.8 + 1.8 + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot-maven-plugin.version} + + + + diff --git a/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/pom.xml b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/pom.xml index 9eede9e92c..a954a7035e 100644 --- a/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/pom.xml +++ b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/pom.xml @@ -17,17 +17,16 @@ - org.springframework.cloud - spring-cloud-starter-stream-rabbit + spring-cloud-starter-stream-rabbit - org.springframework.cloud - spring-cloud-stream-test-support - test - + org.springframework.cloud + spring-cloud-stream-test-support + test + diff --git a/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsServiceApplicationTests.java b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsServiceApplicationTests.java index 18dcd8da7c..196d4f0d23 100644 --- a/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsServiceApplicationTests.java +++ b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsServiceApplicationTests.java @@ -26,13 +26,13 @@ public class MultipleOutputsServiceApplicationTests { private MessageCollector messageCollector; @Test - public void shouldReceiveMessageInAnOutput() { + public void whenSendMessage_thenResponseIsInAOutput() { whenSendMessage(1); thenPayloadInChannelIs(pipe.anOutput(), 1); } @Test - public void shouldReceiveMessageInAnAnotherOutput() { + public void whenSendMessage_thenResponseIsInAnotherOutput() { whenSendMessage(11); thenPayloadInChannelIs(pipe.anotherOutput(), 11); } diff --git a/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsWithConditionsServiceApplicationTests.java b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsWithConditionsServiceApplicationTests.java index 02638dea4c..a74ee61be6 100644 --- a/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsWithConditionsServiceApplicationTests.java +++ b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsWithConditionsServiceApplicationTests.java @@ -26,13 +26,13 @@ public class MultipleOutputsWithConditionsServiceApplicationTests { private MessageCollector messageCollector; @Test - public void shouldReceiveMessageInAnOutput() { + public void whenSendMessage_thenResponseIsInAOutput() { whenSendMessage(1); thenPayloadInChannelIs(pipe.anotherOutput(), 1); } @Test - public void shouldReceiveMessageInAnAnotherOutput() { + public void whenSendMessage_thenResponseIsInAnotherOutput() { whenSendMessage(11); thenPayloadInChannelIs(pipe.anotherOutput(), 11); } diff --git a/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MyLoggerApplicationTests.java b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MyLoggerApplicationTests.java index d004987929..b939af25fc 100644 --- a/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MyLoggerApplicationTests.java +++ b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MyLoggerApplicationTests.java @@ -12,7 +12,6 @@ import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import com.baeldung.spring.cloud.stream.rabbit.MyLoggerServiceApplication; import com.baeldung.spring.cloud.stream.rabbit.model.LogMessage; @RunWith(SpringJUnit4ClassRunner.class) @@ -27,18 +26,15 @@ public class MyLoggerApplicationTests { private MessageCollector messageCollector; @Test - public void shouldEnrichMessage() { - // Send message + public void whenSendMessage_thenResponseShouldUpdateText() { pipe.input() .send(MessageBuilder.withPayload(new LogMessage("This is my message")) .build()); - // Get response from the service Object payload = messageCollector.forChannel(pipe.output()) .poll() .getPayload(); - // Assert assertEquals("[1]: This is my message", payload.toString()); } } From 65d6e0127fa31ae92f0aaa25a96ee9b6c4f2972e Mon Sep 17 00:00:00 2001 From: Graham Cox Date: Tue, 24 Oct 2017 12:51:14 +0100 Subject: [PATCH 13/50] Updated test names --- .../baeldung/kotlin/junit5/CalculatorTest5.kt | 20 +++++++++---------- .../com/baeldung/kotlin/junit5/SimpleTest5.kt | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/core-kotlin/src/test/kotlin/com/baeldung/kotlin/junit5/CalculatorTest5.kt b/core-kotlin/src/test/kotlin/com/baeldung/kotlin/junit5/CalculatorTest5.kt index dd35805044..40cd9adc99 100644 --- a/core-kotlin/src/test/kotlin/com/baeldung/kotlin/junit5/CalculatorTest5.kt +++ b/core-kotlin/src/test/kotlin/com/baeldung/kotlin/junit5/CalculatorTest5.kt @@ -7,12 +7,12 @@ class CalculatorTest5 { private val calculator = Calculator() @Test - fun testAddition() { + fun whenAdding1and3_thenAnswerIs4() { Assertions.assertEquals(4, calculator.add(1, 3)) } @Test - fun testDivideByZero() { + fun whenDividingBy0_thenErrorOccurs() { val exception = Assertions.assertThrows(DivideByZeroException::class.java) { calculator.divide(5, 0) } @@ -21,7 +21,7 @@ class CalculatorTest5 { } @Test - fun testSquares() { + fun whenSquaringNumbers_thenCorrectAnswerGiven() { Assertions.assertAll( Executable { Assertions.assertEquals(1, calculator.square(1)) }, Executable { Assertions.assertEquals(4, calculator.square(2)) }, @@ -31,9 +31,9 @@ class CalculatorTest5 { @TestFactory fun testSquaresFactory() = listOf( - DynamicTest.dynamicTest("1 squared") { Assertions.assertEquals(1,calculator.square(1))}, - DynamicTest.dynamicTest("2 squared") { Assertions.assertEquals(4,calculator.square(2))}, - DynamicTest.dynamicTest("3 squared") { Assertions.assertEquals(9,calculator.square(3))} + DynamicTest.dynamicTest("when I calculate 1^2 then I get 1") { Assertions.assertEquals(1,calculator.square(1))}, + DynamicTest.dynamicTest("when I calculate 2^2 then I get 4") { Assertions.assertEquals(4,calculator.square(2))}, + DynamicTest.dynamicTest("when I calculate 3^2 then I get 9") { Assertions.assertEquals(9,calculator.square(3))} ) @TestFactory @@ -44,7 +44,7 @@ class CalculatorTest5 { 4 to 16, 5 to 25) .map { (input, expected) -> - DynamicTest.dynamicTest("$input squared") { + DynamicTest.dynamicTest("when I calculate $input^2 then I get $expected") { Assertions.assertEquals(expected, calculator.square(input)) } } @@ -59,14 +59,14 @@ class CalculatorTest5 { @TestFactory fun testSquaresFactory3() = squaresTestData .map { (input, expected) -> - DynamicTest.dynamicTest("$input squared") { + DynamicTest.dynamicTest("when I calculate $input^2 then I get $expected") { Assertions.assertEquals(expected, calculator.square(input)) } } @TestFactory fun testSquareRootsFactory3() = squaresTestData .map { (expected, input) -> - DynamicTest.dynamicTest("Square root of $input") { + DynamicTest.dynamicTest("I calculate the square root of $input then I get $expected") { Assertions.assertEquals(expected.toDouble(), calculator.squareRoot(input)) } } @@ -76,7 +76,7 @@ class CalculatorTest5 { Tag("logarithms") ) @Test - fun testLogarithms() { + fun whenIcalculateLog2Of8_thenIget3() { Assertions.assertEquals(3.0, calculator.log(2, 8)) } } diff --git a/core-kotlin/src/test/kotlin/com/baeldung/kotlin/junit5/SimpleTest5.kt b/core-kotlin/src/test/kotlin/com/baeldung/kotlin/junit5/SimpleTest5.kt index c04ab568f7..70d3fb90bf 100644 --- a/core-kotlin/src/test/kotlin/com/baeldung/kotlin/junit5/SimpleTest5.kt +++ b/core-kotlin/src/test/kotlin/com/baeldung/kotlin/junit5/SimpleTest5.kt @@ -6,14 +6,14 @@ import org.junit.jupiter.api.Test class SimpleTest5 { @Test - fun testEmpty() { + fun whenEmptyList_thenListIsEmpty() { val list = listOf() Assertions.assertTrue(list::isEmpty) } @Test @Disabled - fun testMessage() { + fun when3equals4_thenTestFails() { Assertions.assertEquals(3, 4) { "Three does not equal four" } From d7c207145ad799983f5ade9bd1a8b7787258f1d2 Mon Sep 17 00:00:00 2001 From: Jose Carvajal Date: Mon, 30 Oct 2017 13:39:39 +0100 Subject: [PATCH 14/50] Renamed unit tests --- ...ts.java => MultipleOutputsServiceApplicationUnitTest.java} | 2 +- ...java => MultipleOutputsWithConditionsServiceUnitTest.java} | 4 ++-- ...ApplicationTests.java => MyLoggerApplicationUnitTest.java} | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/{MultipleOutputsServiceApplicationTests.java => MultipleOutputsServiceApplicationUnitTest.java} (96%) rename spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/{MultipleOutputsWithConditionsServiceApplicationTests.java => MultipleOutputsWithConditionsServiceUnitTest.java} (92%) rename spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/{MyLoggerApplicationTests.java => MyLoggerApplicationUnitTest.java} (96%) diff --git a/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsServiceApplicationTests.java b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsServiceApplicationUnitTest.java similarity index 96% rename from spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsServiceApplicationTests.java rename to spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsServiceApplicationUnitTest.java index 196d4f0d23..225ed73ff7 100644 --- a/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsServiceApplicationTests.java +++ b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsServiceApplicationUnitTest.java @@ -17,7 +17,7 @@ import com.baeldung.spring.cloud.stream.rabbit.processor.MyProcessor; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = MultipleOutputsServiceApplication.class) @DirtiesContext -public class MultipleOutputsServiceApplicationTests { +public class MultipleOutputsServiceApplicationUnitTest { @Autowired private MyProcessor pipe; diff --git a/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsWithConditionsServiceApplicationTests.java b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsWithConditionsServiceUnitTest.java similarity index 92% rename from spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsWithConditionsServiceApplicationTests.java rename to spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsWithConditionsServiceUnitTest.java index a74ee61be6..8628b209cb 100644 --- a/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsWithConditionsServiceApplicationTests.java +++ b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsWithConditionsServiceUnitTest.java @@ -17,7 +17,7 @@ import com.baeldung.spring.cloud.stream.rabbit.processor.MyProcessor; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = MultipleOutputsWithConditionsServiceApplication.class) @DirtiesContext -public class MultipleOutputsWithConditionsServiceApplicationTests { +public class MultipleOutputsWithConditionsServiceUnitTest { @Autowired private MyProcessor pipe; @@ -28,7 +28,7 @@ public class MultipleOutputsWithConditionsServiceApplicationTests { @Test public void whenSendMessage_thenResponseIsInAOutput() { whenSendMessage(1); - thenPayloadInChannelIs(pipe.anotherOutput(), 1); + thenPayloadInChannelIs(pipe.anOutput(), 1); } @Test diff --git a/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MyLoggerApplicationTests.java b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MyLoggerApplicationUnitTest.java similarity index 96% rename from spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MyLoggerApplicationTests.java rename to spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MyLoggerApplicationUnitTest.java index b939af25fc..1541af3cb5 100644 --- a/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MyLoggerApplicationTests.java +++ b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MyLoggerApplicationUnitTest.java @@ -17,7 +17,7 @@ import com.baeldung.spring.cloud.stream.rabbit.model.LogMessage; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = MyLoggerServiceApplication.class) @DirtiesContext -public class MyLoggerApplicationTests { +public class MyLoggerApplicationUnitTest { @Autowired private Processor pipe; From 449c107ef224dad82f7814c0965b69f632c7b894 Mon Sep 17 00:00:00 2001 From: "Hany.Ahmed" Date: Mon, 20 Nov 2017 21:46:29 +0200 Subject: [PATCH 15/50] BAEL-1184 Quick Guide to BDDMockito --- .../baeldung/bddmockito/BDDMockitoTest.java | 104 ++++++++++++++++++ .../bddmockito/PhoneBookRepository.java | 26 +++++ .../baeldung/bddmockito/PhoneBookService.java | 34 ++++++ 3 files changed, 164 insertions(+) create mode 100644 testing-modules/mockito/src/test/java/org/baeldung/bddmockito/BDDMockitoTest.java create mode 100644 testing-modules/mockito/src/test/java/org/baeldung/bddmockito/PhoneBookRepository.java create mode 100644 testing-modules/mockito/src/test/java/org/baeldung/bddmockito/PhoneBookService.java diff --git a/testing-modules/mockito/src/test/java/org/baeldung/bddmockito/BDDMockitoTest.java b/testing-modules/mockito/src/test/java/org/baeldung/bddmockito/BDDMockitoTest.java new file mode 100644 index 0000000000..9cc586fb84 --- /dev/null +++ b/testing-modules/mockito/src/test/java/org/baeldung/bddmockito/BDDMockitoTest.java @@ -0,0 +1,104 @@ +package org.baeldung.bddmockito; + +import static org.junit.Assert.fail; +import static org.mockito.BDDMockito.*; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; + + +public class BDDMockitoTest { + + PhoneBookService phoneBookService; + PhoneBookRepository phoneBookRepository; + + String momContactName = "Mom"; + String momPhoneNumber = "01234"; + String xContactName = "x"; + String tooLongPhoneNumber = "01111111111111"; + + @Before + public void init() { + phoneBookRepository = Mockito.mock(PhoneBookRepository.class); + phoneBookService = new PhoneBookService(phoneBookRepository); + } + + @Test + public void givenValidContactName_whenSearchInPhoneBook_thenRetunPhoneNumber() { + given(phoneBookRepository.contains(momContactName)).willReturn(true); + given(phoneBookRepository.getPhoneNumberByContactName(momContactName)) + .will((InvocationOnMock invocation) -> { + if(invocation.getArgument(0).equals(momContactName)) { + return momPhoneNumber; + } else { + return null; + } + }); + + String phoneNumber = phoneBookService.search(momContactName); + + then(phoneBookRepository).should().contains(momContactName); + then(phoneBookRepository).should().getPhoneNumberByContactName(momContactName); + Assert.assertEquals(phoneNumber, momPhoneNumber); + } + + @Test + public void givenInvalidContactName_whenSearch_thenRetunNull() { + given(phoneBookRepository.contains(xContactName)).willReturn(false); + + String phoneNumber = phoneBookService.search(xContactName); + + then(phoneBookRepository).should().contains(xContactName); + then(phoneBookRepository).should(never()).getPhoneNumberByContactName(xContactName); + Assert.assertEquals(phoneNumber, null); + } + + @Test + public void givenValidContactNameAndPhoneNumber_whenRegister_thenSucceed() { + given(phoneBookRepository.contains(momContactName)).willReturn(false); + + phoneBookService.register(momContactName, momPhoneNumber); + + verify(phoneBookRepository).insert(momContactName, momPhoneNumber); + } + + @Test + public void givenEmptyPhoneNumber_whenRegister_thenFail() { + given(phoneBookRepository.contains(momContactName)).willReturn(false); + + phoneBookService.register(xContactName, ""); + + then(phoneBookRepository).should(never()).insert(momContactName, momPhoneNumber); + } + + @Test + public void givenLongPhoneNumber_whenRegister_thenFail() { + given(phoneBookRepository.contains(xContactName)).willReturn(false); + willThrow(new RuntimeException()) + .given(phoneBookRepository).insert(any(String.class), eq(tooLongPhoneNumber)); + + try { + phoneBookService.register(xContactName, tooLongPhoneNumber); + fail("Should throw exception"); + } catch (RuntimeException ex) { } + + then(phoneBookRepository).should(never()).insert(momContactName, tooLongPhoneNumber); + } + + @Test + public void givenExistentContactName_whenRegister_thenFail() { + given(phoneBookRepository.contains(momContactName)) + .willThrow(new RuntimeException("Name already exist")); + + try { + phoneBookService.register(momContactName, momPhoneNumber); + fail("Should throw exception"); + } catch(Exception ex) { } + + then(phoneBookRepository).should(never()).insert(momContactName, momPhoneNumber); + } + +} diff --git a/testing-modules/mockito/src/test/java/org/baeldung/bddmockito/PhoneBookRepository.java b/testing-modules/mockito/src/test/java/org/baeldung/bddmockito/PhoneBookRepository.java new file mode 100644 index 0000000000..b73a1d835c --- /dev/null +++ b/testing-modules/mockito/src/test/java/org/baeldung/bddmockito/PhoneBookRepository.java @@ -0,0 +1,26 @@ +package org.baeldung.bddmockito; + +public interface PhoneBookRepository { + + /** + * Insert phone record + * @param name Contact name + * @param phone Phone number + */ + void insert(String name, String phone); + + /** + * Search for contact phone number + * @param name Contact name + * @return phone number + */ + String getPhoneNumberByContactName(String name); + + /** + * Check if the phonebook contains this contact + * @param name Contact name + * @return true if this contact name exists + */ + boolean contains(String name); + +} diff --git a/testing-modules/mockito/src/test/java/org/baeldung/bddmockito/PhoneBookService.java b/testing-modules/mockito/src/test/java/org/baeldung/bddmockito/PhoneBookService.java new file mode 100644 index 0000000000..645884af02 --- /dev/null +++ b/testing-modules/mockito/src/test/java/org/baeldung/bddmockito/PhoneBookService.java @@ -0,0 +1,34 @@ +package org.baeldung.bddmockito; + +public class PhoneBookService { + + private PhoneBookRepository phoneBookRepository; + + public PhoneBookService(PhoneBookRepository phoneBookRepository) { + this.phoneBookRepository = phoneBookRepository; + } + + /** + * Register a contact + * @param name Contact name + * @param phone Phone number + */ + public void register(String name, String phone) { + if(!name.isEmpty() && !phone.isEmpty() && !phoneBookRepository.contains(name)) { + phoneBookRepository.insert(name, phone); + } + } + + /** + * Search for a phone number by contact name + * @param name Contact name + * @return Phone number + */ + public String search(String name) { + if(!name.isEmpty() && phoneBookRepository.contains(name)) { + return phoneBookRepository.getPhoneNumberByContactName(name); + } + return null; + } + +} From 043ea5e574ab5deee7c0935f92121dc6630fb7a2 Mon Sep 17 00:00:00 2001 From: "parth.karia" Date: Mon, 27 Nov 2017 12:03:43 +0530 Subject: [PATCH 16/50] Removes jgrapht from libraries --- libraries/pom.xml | 11 +-- .../baeldung/jgrapht/CompleteGraphTest.java | 38 -------- .../baeldung/jgrapht/DirectedGraphTests.java | 95 ------------------- .../baeldung/jgrapht/EulerianCircuitTest.java | 42 -------- 4 files changed, 1 insertion(+), 185 deletions(-) delete mode 100644 libraries/src/test/java/com/baeldung/jgrapht/CompleteGraphTest.java delete mode 100644 libraries/src/test/java/com/baeldung/jgrapht/DirectedGraphTests.java delete mode 100644 libraries/src/test/java/com/baeldung/jgrapht/EulerianCircuitTest.java diff --git a/libraries/pom.xml b/libraries/pom.xml index bf90e4ccd9..0b707b914e 100644 --- a/libraries/pom.xml +++ b/libraries/pom.xml @@ -587,21 +587,12 @@ hazelcast ${hazelcast.version} - - org.jgrapht - jgrapht-core - 1.0.1 - com.netopyr.wurmloch wurmloch-crdt ${crdt.version} - org.jgrapht - jgrapht-core - 1.0.1 - org.docx4j docx4j 3.3.5 @@ -715,4 +706,4 @@ 3.8.4 2.5.5 - + \ No newline at end of file diff --git a/libraries/src/test/java/com/baeldung/jgrapht/CompleteGraphTest.java b/libraries/src/test/java/com/baeldung/jgrapht/CompleteGraphTest.java deleted file mode 100644 index c085d54689..0000000000 --- a/libraries/src/test/java/com/baeldung/jgrapht/CompleteGraphTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.baeldung.jgrapht; - -import static org.junit.Assert.assertEquals; - -import java.util.List; - -import org.jgrapht.VertexFactory; -import org.jgrapht.alg.HamiltonianCycle; -import org.jgrapht.generate.CompleteGraphGenerator; -import org.jgrapht.graph.DefaultEdge; -import org.jgrapht.graph.SimpleWeightedGraph; -import org.junit.Before; -import org.junit.Test; - -public class CompleteGraphTest { - - static SimpleWeightedGraph completeGraph; - static int size = 10; - - @Before - public void createCompleteGraph() { - completeGraph = new SimpleWeightedGraph<>(DefaultEdge.class); - CompleteGraphGenerator completeGenerator = new CompleteGraphGenerator(size); - VertexFactory vFactory = new VertexFactory() { - private int id = 0; - public String createVertex() { - return "v" + id++; - } - }; - completeGenerator.generateGraph(completeGraph, vFactory, null); - } - - @Test - public void givenCompleteGraph_whenGetHamiltonianCyclePath_thenGetVerticeListInSequence() { - List verticeList = HamiltonianCycle.getApproximateOptimalForCompleteGraph(completeGraph); - assertEquals(verticeList.size(), completeGraph.vertexSet().size()); - } -} diff --git a/libraries/src/test/java/com/baeldung/jgrapht/DirectedGraphTests.java b/libraries/src/test/java/com/baeldung/jgrapht/DirectedGraphTests.java deleted file mode 100644 index 7f4cc99715..0000000000 --- a/libraries/src/test/java/com/baeldung/jgrapht/DirectedGraphTests.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.baeldung.jgrapht; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.stream.IntStream; - -import org.jgrapht.DirectedGraph; -import org.jgrapht.GraphPath; -import org.jgrapht.alg.CycleDetector; -import org.jgrapht.alg.KosarajuStrongConnectivityInspector; -import org.jgrapht.alg.interfaces.StrongConnectivityAlgorithm; -import org.jgrapht.alg.shortestpath.AllDirectedPaths; -import org.jgrapht.alg.shortestpath.BellmanFordShortestPath; -import org.jgrapht.alg.shortestpath.DijkstraShortestPath; -import org.jgrapht.graph.DefaultDirectedGraph; -import org.jgrapht.graph.DefaultEdge; -import org.jgrapht.graph.DirectedSubgraph; -import org.jgrapht.traverse.BreadthFirstIterator; -import org.jgrapht.traverse.DepthFirstIterator; -import org.junit.Before; -import org.junit.Test; - -public class DirectedGraphTests { - DirectedGraph directedGraph; - - @Before - public void createDirectedGraph() { - directedGraph = new DefaultDirectedGraph(DefaultEdge.class); - IntStream.range(1, 10).forEach(i -> { - directedGraph.addVertex("v" + i); - }); - directedGraph.addEdge("v1", "v2"); - directedGraph.addEdge("v2", "v4"); - directedGraph.addEdge("v4", "v3"); - directedGraph.addEdge("v3", "v1"); - directedGraph.addEdge("v5", "v4"); - directedGraph.addEdge("v5", "v6"); - directedGraph.addEdge("v6", "v7"); - directedGraph.addEdge("v7", "v5"); - directedGraph.addEdge("v8", "v5"); - directedGraph.addEdge("v9", "v8"); - } - - @Test - public void givenDirectedGraph_whenGetStronglyConnectedSubgraphs_thenPathExistsBetweenStronglyconnectedVertices() { - StrongConnectivityAlgorithm scAlg = new KosarajuStrongConnectivityInspector<>(directedGraph); - List> stronglyConnectedSubgraphs = scAlg.stronglyConnectedSubgraphs(); - List stronglyConnectedVertices = new ArrayList<>(stronglyConnectedSubgraphs.get(3).vertexSet()); - - String randomVertex1 = stronglyConnectedVertices.get(0); - String randomVertex2 = stronglyConnectedVertices.get(3); - AllDirectedPaths allDirectedPaths = new AllDirectedPaths<>(directedGraph); - - List> possiblePathList = allDirectedPaths.getAllPaths(randomVertex1, randomVertex2, false, stronglyConnectedVertices.size()); - assertTrue(possiblePathList.size() > 0); - } - - @Test - public void givenDirectedGraphWithCycle_whenCheckCycles_thenDetectCycles() { - CycleDetector cycleDetector = new CycleDetector(directedGraph); - assertTrue(cycleDetector.detectCycles()); - Set cycleVertices = cycleDetector.findCycles(); - assertTrue(cycleVertices.size() > 0); - } - - @Test - public void givenDirectedGraph_whenCreateInstanceDepthFirstIterator_thenGetIterator() { - DepthFirstIterator depthFirstIterator = new DepthFirstIterator<>(directedGraph); - assertNotNull(depthFirstIterator); - } - - @Test - public void givenDirectedGraph_whenCreateInstanceBreadthFirstIterator_thenGetIterator() { - BreadthFirstIterator breadthFirstIterator = new BreadthFirstIterator<>(directedGraph); - assertNotNull(breadthFirstIterator); - } - - @Test - public void givenDirectedGraph_whenGetDijkstraShortestPath_thenGetNotNullPath() { - DijkstraShortestPath dijkstraShortestPath = new DijkstraShortestPath(directedGraph); - List shortestPath = dijkstraShortestPath.getPath("v1", "v4").getVertexList(); - assertNotNull(shortestPath); - } - - @Test - public void givenDirectedGraph_whenGetBellmanFordShortestPath_thenGetNotNullPath() { - BellmanFordShortestPath bellmanFordShortestPath = new BellmanFordShortestPath(directedGraph); - List shortestPath = bellmanFordShortestPath.getPath("v1", "v4").getVertexList(); - assertNotNull(shortestPath); - } -} diff --git a/libraries/src/test/java/com/baeldung/jgrapht/EulerianCircuitTest.java b/libraries/src/test/java/com/baeldung/jgrapht/EulerianCircuitTest.java deleted file mode 100644 index 6f0fb92ab7..0000000000 --- a/libraries/src/test/java/com/baeldung/jgrapht/EulerianCircuitTest.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.baeldung.jgrapht; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.util.stream.IntStream; - -import org.jgrapht.GraphPath; -import org.jgrapht.alg.cycle.HierholzerEulerianCycle; -import org.jgrapht.graph.DefaultEdge; -import org.jgrapht.graph.SimpleWeightedGraph; -import org.junit.Before; -import org.junit.Test; - -public class EulerianCircuitTest { - SimpleWeightedGraph simpleGraph; - - @Before - public void createGraphWithEulerianCircuit() { - simpleGraph = new SimpleWeightedGraph<>(DefaultEdge.class); - IntStream.range(1, 6).forEach(i -> { - simpleGraph.addVertex("v" + i); - }); - IntStream.range(1, 6).forEach(i -> { - int endVertexNo = (i + 1) > 5 ? 1 : i + 1; - simpleGraph.addEdge("v" + i, "v" + endVertexNo); - }); - } - - @Test - public void givenGraph_whenCheckEluerianCycle_thenGetResult() { - HierholzerEulerianCycle eulerianCycle = new HierholzerEulerianCycle<>(); - assertTrue(eulerianCycle.isEulerian(simpleGraph)); - } - - @Test - public void givenGraphWithEulerianCircuit_whenGetEulerianCycle_thenGetGraphPath() { - HierholzerEulerianCycle eulerianCycle = new HierholzerEulerianCycle<>(); - GraphPath path = eulerianCycle.getEulerianCycle(simpleGraph); - assertTrue(path.getEdgeList().containsAll(simpleGraph.edgeSet())); - } -} From b1782b3c19c3f4e3eef2a976efa23ad066d28e00 Mon Sep 17 00:00:00 2001 From: "parth.karia" Date: Mon, 27 Nov 2017 12:05:18 +0530 Subject: [PATCH 17/50] Resolve conflicts manually --- libraries/pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/pom.xml b/libraries/pom.xml index 0b707b914e..27d867b68b 100644 --- a/libraries/pom.xml +++ b/libraries/pom.xml @@ -587,6 +587,11 @@ hazelcast ${hazelcast.version} + + org.jgrapht + jgrapht-core + 1.0.1 + com.netopyr.wurmloch wurmloch-crdt From 3daa1b01ce4794f2df03ad0bf44c87b5378f9438 Mon Sep 17 00:00:00 2001 From: "parth.karia" Date: Mon, 27 Nov 2017 12:10:19 +0530 Subject: [PATCH 18/50] BAEL-21 Exploring the new HTTP Client in Java 9 --- .../java9/httpclient/HttpClientExample.java | 76 +++++++++++++++++++ core-java-9/src/main/java/module-info.java | 3 + 2 files changed, 79 insertions(+) create mode 100644 core-java-9/src/main/java/com/baeldung/java9/httpclient/HttpClientExample.java create mode 100644 core-java-9/src/main/java/module-info.java diff --git a/core-java-9/src/main/java/com/baeldung/java9/httpclient/HttpClientExample.java b/core-java-9/src/main/java/com/baeldung/java9/httpclient/HttpClientExample.java new file mode 100644 index 0000000000..6ccfd030a5 --- /dev/null +++ b/core-java-9/src/main/java/com/baeldung/java9/httpclient/HttpClientExample.java @@ -0,0 +1,76 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.baeldung.java9.httpclient; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; +import jdk.incubator.http.HttpClient; +import jdk.incubator.http.HttpRequest; +import jdk.incubator.http.HttpRequest.BodyProcessor; +import jdk.incubator.http.HttpResponse; +import jdk.incubator.http.HttpResponse.BodyHandler; + +/** + * + * @author pkaria + */ +public class HttpClientExample { + + public void httpGetRequest() throws URISyntaxException, IOException, InterruptedException { + HttpClient client = HttpClient.newHttpClient(); + URI httpURI = new URI("http://jsonplaceholder.typicode.com/posts/1"); + HttpRequest request = HttpRequest.newBuilder(httpURI).GET().build(); + HttpResponse response = client.send(request, HttpResponse.BodyHandler.asString()); + String responseBody = response.body(); + int responseStatusCode = response.statusCode(); + System.out.println(responseBody); + } + + public void httpPosttRequest() throws URISyntaxException, IOException, InterruptedException { + HttpClient client = HttpClient + .newBuilder() + .build(); + HttpRequest request = HttpRequest + .newBuilder(new URI("http://jsonplaceholder.typicode.com/posts")) + .POST(BodyProcessor.fromString("Sample Post Request")) + .build(); + HttpResponse response + = client.send(request, HttpResponse.BodyHandler.asString()); + String responseBody = response.body(); + System.out.println(responseBody); + } + + public void asynchronousRequest() throws URISyntaxException { + HttpClient client = HttpClient.newHttpClient(); + URI httpURI = new URI("http://jsonplaceholder.typicode.com/posts/1"); + HttpRequest request = HttpRequest.newBuilder(httpURI).GET().build(); + CompletableFuture> futureResponse = client.sendAsync(request, + HttpResponse.BodyHandler.asString()); + } + + public void asynchronousMultipleRequests() throws URISyntaxException { + List targets = Arrays.asList(new URI("http://jsonplaceholder.typicode.com/posts/1"), new URI("http://jsonplaceholder.typicode.com/posts/2")); + HttpClient client = HttpClient.newHttpClient(); + List> futures = targets + .stream() + .map(target -> client + .sendAsync( + HttpRequest.newBuilder(target) + .GET() + .build(), + BodyHandler.asFile(Paths.get("base", target.getPath()))) + .thenApply(response -> response.body()) + .thenApply(path -> path.toFile())) + .collect(Collectors.toList()); + } +} diff --git a/core-java-9/src/main/java/module-info.java b/core-java-9/src/main/java/module-info.java new file mode 100644 index 0000000000..163dd4f5be --- /dev/null +++ b/core-java-9/src/main/java/module-info.java @@ -0,0 +1,3 @@ +module com.baeldung.java9.httpclient { + requires jdk.incubator.httpclient; +} \ No newline at end of file From d26adf690b615d5194b5f1650b092c68139445c3 Mon Sep 17 00:00:00 2001 From: Markus Gulden Date: Wed, 29 Nov 2017 23:50:00 +0100 Subject: [PATCH 19/50] BAEL-1336 Introduction to Hibernate Search --- .../spring-hibernate-5/pom.xml | 6 + .../HibernateSearchConfig.java | 76 +++++++ .../hibernatesearch/ProductSearchDao.java | 195 ++++++++++++++++++ .../hibernatesearch/model/Product.java | 94 +++++++++ .../main/resources/persistence-h2.properties | 4 + .../HibernateSearchIntegrationTest.java | 187 +++++++++++++++++ 6 files changed, 562 insertions(+) create mode 100644 persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/hibernatesearch/HibernateSearchConfig.java create mode 100644 persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/hibernatesearch/ProductSearchDao.java create mode 100644 persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/hibernatesearch/model/Product.java create mode 100644 persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernatesearch/HibernateSearchIntegrationTest.java diff --git a/persistence-modules/spring-hibernate-5/pom.xml b/persistence-modules/spring-hibernate-5/pom.xml index f1f3d10347..8dc447c3b7 100644 --- a/persistence-modules/spring-hibernate-5/pom.xml +++ b/persistence-modules/spring-hibernate-5/pom.xml @@ -57,6 +57,11 @@ jta ${jta.version} + + org.hibernate + hibernate-search-orm + ${hibernatesearch.version} + org.apache.tomcat @@ -184,6 +189,7 @@ 5.2.10.Final + 5.8.2.Final 8.0.7-dmr 9.0.0.M26 1.1 diff --git a/persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/hibernatesearch/HibernateSearchConfig.java b/persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/hibernatesearch/HibernateSearchConfig.java new file mode 100644 index 0000000000..6bbd2625fc --- /dev/null +++ b/persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/hibernatesearch/HibernateSearchConfig.java @@ -0,0 +1,76 @@ +package com.baeldung.hibernatesearch; + +import com.google.common.base.Preconditions; +import org.apache.tomcat.dbcp.dbcp2.BasicDataSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import org.springframework.core.env.Environment; +import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.JpaVendorAdapter; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +import javax.persistence.EntityManagerFactory; +import javax.sql.DataSource; +import java.util.Properties; + +@EnableTransactionManagement +@Configuration +@PropertySource({ "classpath:persistence-h2.properties" }) +@EnableJpaRepositories(basePackages = { "com.baeldung.hibernatesearch" }) +@ComponentScan({ "com.baeldung.hibernatesearch" }) +public class HibernateSearchConfig { + + @Autowired + private Environment env; + + @Bean + public LocalContainerEntityManagerFactoryBean entityManagerFactory() { + LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); + em.setDataSource(dataSource()); + em.setPackagesToScan(new String[] { "com.baeldung.hibernatesearch.model" }); + + JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); + em.setJpaVendorAdapter(vendorAdapter); + em.setJpaProperties(additionalProperties()); + + return em; + } + + @Bean + public DataSource dataSource() { + final BasicDataSource dataSource = new BasicDataSource(); + dataSource.setDriverClassName(Preconditions.checkNotNull(env.getProperty("jdbc.driverClassName"))); + dataSource.setUrl(Preconditions.checkNotNull(env.getProperty("jdbc.url"))); + + return dataSource; + } + + @Bean + public PlatformTransactionManager transactionManager(EntityManagerFactory emf) { + JpaTransactionManager transactionManager = new JpaTransactionManager(); + transactionManager.setEntityManagerFactory(emf); + + return transactionManager; + } + + @Bean + public PersistenceExceptionTranslationPostProcessor exceptionTranslation() { + return new PersistenceExceptionTranslationPostProcessor(); + } + + Properties additionalProperties() { + Properties properties = new Properties(); + properties.setProperty("hibernate.hbm2ddl.auto", Preconditions.checkNotNull(env.getProperty("hibernate.hbm2ddl.auto"))); + properties.setProperty("hibernate.dialect", Preconditions.checkNotNull(env.getProperty("hibernate.dialect"))); + return properties; + } +} \ No newline at end of file diff --git a/persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/hibernatesearch/ProductSearchDao.java b/persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/hibernatesearch/ProductSearchDao.java new file mode 100644 index 0000000000..210c1c58b3 --- /dev/null +++ b/persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/hibernatesearch/ProductSearchDao.java @@ -0,0 +1,195 @@ +package com.baeldung.hibernatesearch; + +import com.baeldung.hibernatesearch.model.Product; +import org.apache.lucene.search.Query; +import org.hibernate.search.engine.ProjectionConstants; +import org.hibernate.search.jpa.FullTextEntityManager; +import org.hibernate.search.jpa.FullTextQuery; +import org.hibernate.search.jpa.Search; +import org.hibernate.search.query.dsl.QueryBuilder; +import org.springframework.stereotype.Repository; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import java.util.List; + +@Repository +public class ProductSearchDao { + + @PersistenceContext + private EntityManager entityManager; + + public List searchProductNameByKeywordQuery(String text) { + + Query keywordQuery = getQueryBuilder() + .keyword() + .onField("productName") + .matching(text) + .createQuery(); + + List results = getJpaQuery(keywordQuery).getResultList(); + + return results; + } + + public List searchProductNameByFuzzyQuery(String text) { + + Query fuzzyQuery = getQueryBuilder() + .keyword() + .fuzzy() + .withEditDistanceUpTo(2) + .withPrefixLength(0) + .onField("productName") + .matching(text) + .createQuery(); + + List results = getJpaQuery(fuzzyQuery).getResultList(); + + return results; + } + + public List searchProductNameByWildcardQuery(String text) { + + Query wildcardQuery = getQueryBuilder() + .keyword() + .wildcard() + .onField("productName") + .matching(text) + .createQuery(); + + List results = getJpaQuery(wildcardQuery).getResultList(); + + return results; + } + + public List searchProductDescriptionByPhraseQuery(String text) { + + Query phraseQuery = getQueryBuilder() + .phrase() + .withSlop(1) + .onField("description") + .sentence(text) + .createQuery(); + + List results = getJpaQuery(phraseQuery).getResultList(); + + return results; + } + + public List searchProductNameAndDescriptionBySimpleQueryStringQuery(String text) { + + Query simpleQueryStringQuery = getQueryBuilder() + .simpleQueryString() + .onFields("productName", "description") + .matching(text) + .createQuery(); + + List results = getJpaQuery(simpleQueryStringQuery).getResultList(); + + return results; + } + + public List searchProductNameByRangeQuery(int low, int high) { + + Query rangeQuery = getQueryBuilder() + .range() + .onField("memory") + .from(low) + .to(high) + .createQuery(); + + List results = getJpaQuery(rangeQuery).getResultList(); + + return results; + } + + public List searchProductNameByMoreLikeThisQuery(Product entity) { + + Query moreLikeThisQuery = getQueryBuilder() + .moreLikeThis() + .comparingField("productName") + .toEntity(entity) + .createQuery(); + + List results = getJpaQuery(moreLikeThisQuery).setProjection(ProjectionConstants.THIS, ProjectionConstants.SCORE) + .getResultList(); + + return results; + } + + public List searchProductNameAndDescriptionByKeywordQuery(String text) { + + Query keywordQuery = getQueryBuilder() + .keyword() + .onFields("productName", "description") + .matching(text) + .createQuery(); + + List results = getJpaQuery(keywordQuery).getResultList(); + + return results; + } + + public List searchProductNameAndDescriptionByMoreLikeThisQuery(Product entity) { + + Query moreLikeThisQuery = getQueryBuilder() + .moreLikeThis() + .comparingField("productName") + .toEntity(entity) + .createQuery(); + + List results = getJpaQuery(moreLikeThisQuery).setProjection(ProjectionConstants.THIS, ProjectionConstants.SCORE) + .getResultList(); + + return results; + } + + public List searchProductNameAndDescriptionByCombinedQuery(String manufactorer, int memoryLow, int memoryTop, String extraFeature, String exclude) { + + Query combinedQuery = getQueryBuilder() + .bool() + .must(getQueryBuilder().keyword() + .onField("productName") + .matching(manufactorer) + .createQuery()) + .must(getQueryBuilder() + .range() + .onField("memory") + .from(memoryLow) + .to(memoryTop) + .createQuery()) + .should(getQueryBuilder() + .phrase() + .onField("description") + .sentence(extraFeature) + .createQuery()) + .must(getQueryBuilder() + .keyword() + .onField("productName") + .matching(exclude) + .createQuery()) + .not() + .createQuery(); + + List results = getJpaQuery(combinedQuery).getResultList(); + + return results; + } + + private FullTextQuery getJpaQuery(org.apache.lucene.search.Query luceneQuery) { + + FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager); + + return fullTextEntityManager.createFullTextQuery(luceneQuery, Product.class); + } + + private QueryBuilder getQueryBuilder() { + + FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager); + + return fullTextEntityManager.getSearchFactory() + .buildQueryBuilder() + .forEntity(Product.class) + .get(); + } +} diff --git a/persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/hibernatesearch/model/Product.java b/persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/hibernatesearch/model/Product.java new file mode 100644 index 0000000000..3ca020fe57 --- /dev/null +++ b/persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/hibernatesearch/model/Product.java @@ -0,0 +1,94 @@ +package com.baeldung.hibernatesearch.model; + +import org.hibernate.search.annotations.*; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Indexed +@Table(name = "product") +public class Product { + + @Id + private int id; + + @Field(termVector = TermVector.YES) + private String productName; + + @Field(termVector = TermVector.YES) + private String description; + + @Field + private int memory; + + public Product(int id, String productName, int memory, String description) { + this.id = id; + this.productName = productName; + this.memory = memory; + this.description = description; + } + + public Product() { + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof Product)) + return false; + + Product product = (Product) o; + + if (id != product.id) + return false; + if (memory != product.memory) + return false; + if (!productName.equals(product.productName)) + return false; + return description.equals(product.description); + } + + @Override + public int hashCode() { + int result = id; + result = 31 * result + productName.hashCode(); + result = 31 * result + memory; + result = 31 * result + description.hashCode(); + return result; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getProductName() { + return productName; + } + + public void setProductName(String productName) { + this.productName = productName; + } + + public int getMemory() { + return memory; + } + + public void setMemory(int memory) { + this.memory = memory; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } +} diff --git a/persistence-modules/spring-hibernate-5/src/main/resources/persistence-h2.properties b/persistence-modules/spring-hibernate-5/src/main/resources/persistence-h2.properties index 696e805cff..915bc4317b 100644 --- a/persistence-modules/spring-hibernate-5/src/main/resources/persistence-h2.properties +++ b/persistence-modules/spring-hibernate-5/src/main/resources/persistence-h2.properties @@ -9,5 +9,9 @@ hibernate.dialect=org.hibernate.dialect.H2Dialect hibernate.show_sql=false hibernate.hbm2ddl.auto=create-drop +# hibernate.search.X +hibernate.search.default.directory_provider = filesystem +hibernate.search.default.indexBase = /data/index/default + # envers.X envers.audit_table_suffix=_audit_log diff --git a/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernatesearch/HibernateSearchIntegrationTest.java b/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernatesearch/HibernateSearchIntegrationTest.java new file mode 100644 index 0000000000..b69f8d3a60 --- /dev/null +++ b/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernatesearch/HibernateSearchIntegrationTest.java @@ -0,0 +1,187 @@ +package com.baeldung.hibernatesearch; + +import com.baeldung.hibernatesearch.model.Product; + +import static org.hamcrest.collection.IsIterableContainingInOrder.contains; +import static org.junit.Assert.*; + +import org.hibernate.search.jpa.FullTextEntityManager; +import org.hibernate.search.jpa.Search; +import org.junit.Before; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.Commit; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.AnnotationConfigContextLoader; +import org.springframework.transaction.annotation.Transactional; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = { HibernateSearchConfig.class }, loader = AnnotationConfigContextLoader.class) +@Transactional +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class HibernateSearchIntegrationTest { + + @Autowired + ProductSearchDao dao; + + @PersistenceContext + private EntityManager entityManager; + + private List products; + + @Before + public void setupTestData() { + + products = Arrays.asList(new Product(1, "Apple iPhone X 256 GB", 256, "The current high-end smartphone from Apple, with lots of memory and also Face ID"), + new Product(2, "Apple iPhone X 128 GB", 128, "The current high-end smartphone from Apple, with Face ID"), new Product(3, "Apple iPhone 8 128 GB", 128, "The latest smartphone from Apple within the regular iPhone line, supporting wireless charging"), + new Product(4, "Samsung Galaxy S7 128 GB", 64, "A great Android smartphone"), new Product(5, "Microsoft Lumia 650 32 GB", 32, "A cheaper smartphone, coming with Windows Mobile"), + new Product(6, "Microsoft Lumia 640 32 GB", 32, "A cheaper smartphone, coming with Windows Mobile"), new Product(7, "Microsoft Lumia 630 16 GB", 16, "A cheaper smartphone, coming with Windows Mobile")); + } + + @Commit + @Test + public void testA_whenInitialTestDataInserted_thenSuccess() { + + for (int i = 0; i < products.size() - 1; i++) { + entityManager.persist(products.get(i)); + } + } + + @Test + public void testB_whenIndexInitialized_thenCorrectIndexSize() throws InterruptedException { + + FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager); + fullTextEntityManager.createIndexer() + .startAndWait(); + int indexSize = fullTextEntityManager.getSearchFactory() + .getStatistics() + .getNumberOfIndexedEntities(Product.class.getName()); + + assertEquals(products.size() - 1, indexSize); + } + + @Commit + @Test + public void testC_whenAdditionalTestDataInserted_thenSuccess() { + + entityManager.persist(products.get(products.size() - 1)); + } + + @Test + public void testD_whenAdditionalTestDataInserted_thenIndexUpdatedAutomatically() { + + FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager); + int indexSize = fullTextEntityManager.getSearchFactory() + .getStatistics() + .getNumberOfIndexedEntities(Product.class.getName()); + + assertEquals(products.size(), indexSize); + } + + @Test + public void testE_whenKeywordSearchOnName_thenCorrectMatches() { + List expected = Arrays.asList(products.get(0), products.get(1), products.get(2)); + List results = dao.searchProductNameByKeywordQuery("iphone"); + + assertThat(results, containsInAnyOrder(expected.toArray())); + } + + @Test + public void testF_whenFuzzySearch_thenCorrectMatches() { + List expected = Arrays.asList(products.get(0), products.get(1), products.get(2)); + List results = dao.searchProductNameByFuzzyQuery("iPhaen"); + + assertThat(results, containsInAnyOrder(expected.toArray())); + } + + @Test + public void testG_whenWildcardSearch_thenCorrectMatches() { + List expected = Arrays.asList(products.get(4), products.get(5), products.get(6)); + List results = dao.searchProductNameByWildcardQuery("6*"); + + assertThat(results, containsInAnyOrder(expected.toArray())); + + } + + @Test + public void testH_whenPhraseSearch_thenCorrectMatches() { + List expected = Arrays.asList(products.get(2)); + List results = dao.searchProductDescriptionByPhraseQuery("with wireless charging"); + + assertThat(results, containsInAnyOrder(expected.toArray())); + + } + + @Test + public void testI_whenSimpleQueryStringSearch_thenCorrectMatches() { + List expected = Arrays.asList(products.get(0), products.get(1)); + List results = dao.searchProductNameAndDescriptionBySimpleQueryStringQuery("Aple~2 + \"iPhone X\" + (256 | 128)"); + + assertThat(results, containsInAnyOrder(expected.toArray())); + + } + + @Test + public void testJ_whenRangeSearch_thenCorrectMatches() { + List expected = Arrays.asList(products.get(0), products.get(1), products.get(2), products.get(3)); + List results = dao.searchProductNameByRangeQuery(64, 256); + + assertThat(results, containsInAnyOrder(expected.toArray())); + + } + + @Test + public void testK_whenMoreLikeThisSearch_thenCorrectMatchesInOrder() { + List expected = products; + List resultsWithScore = dao.searchProductNameByMoreLikeThisQuery(products.get(0)); + List results = new LinkedList(); + + for (Object[] resultWithScore : resultsWithScore) { + results.add((Product) resultWithScore[0]); + } + + assertThat(results, contains(expected.toArray())); + + } + + @Test + public void testL_whenKeywordSearchOnNameAndDescription_thenCorrectMatches() { + List expected = Arrays.asList(products.get(0), products.get(1), products.get(2)); + List results = dao.searchProductNameAndDescriptionByKeywordQuery("iphone"); + + assertThat(results, containsInAnyOrder(expected.toArray())); + } + + @Test + public void testM_whenMoreLikeThisSearchOnProductNameAndDescription_thenCorrectMatchesInOrder() { + List expected = products; + List resultsWithScore = dao.searchProductNameAndDescriptionByMoreLikeThisQuery(products.get(0)); + List results = new LinkedList(); + + for (Object[] resultWithScore : resultsWithScore) { + results.add((Product) resultWithScore[0]); + } + + assertThat(results, contains(expected.toArray())); + } + + @Test + public void testN_whenCombinedSearch_thenCorrectMatches() { + List expected = Arrays.asList(products.get(1), products.get(2)); + List results = dao.searchProductNameAndDescriptionByCombinedQuery("apple", 64, 128, "face id", "samsung"); + + assertThat(results, containsInAnyOrder(expected.toArray())); + } +} From b14dfbe4f0cd1b4d209730ffa3ba0c2ac7adb7f4 Mon Sep 17 00:00:00 2001 From: Jose Carvajal Date: Thu, 30 Nov 2017 11:55:49 +0100 Subject: [PATCH 20/50] BAEL-1174: A Quick Guide to Spring Cloud Consul --- bootique/dependency-reduced-pom.xml | 8 ++- libraries-data/file.dat | Bin 0 -> 87 bytes libraries/helloWorld.docx | Bin 0 -> 76887 bytes spring-cloud/spring-cloud-consul/pom.xml | 58 ++++++++++++++++++ .../consul/DiscoveryClientApplication.java | 53 ++++++++++++++++ .../DistributedPropertiesApplication.java | 36 +++++++++++ .../cloud/consul/RibbonClientApplication.java | 34 ++++++++++ .../consul/ServiceDiscoveryApplication.java | 29 +++++++++ .../cloud/consul/properties/MyProperties.java | 20 ++++++ .../src/main/resources/application.yml | 14 +++++ .../src/main/resources/bootstrap.yml | 9 +++ undertow/dependency-reduced-pom.xml | 40 ++++++++++++ xml/src/test/resources/example_dom4j_new.xml | 10 +++ xml/src/test/resources/example_jaxb_new.xml | 9 +++ 14 files changed, 319 insertions(+), 1 deletion(-) create mode 100644 libraries-data/file.dat create mode 100644 libraries/helloWorld.docx create mode 100644 spring-cloud/spring-cloud-consul/pom.xml create mode 100644 spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/DiscoveryClientApplication.java create mode 100644 spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/DistributedPropertiesApplication.java create mode 100644 spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/RibbonClientApplication.java create mode 100644 spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/ServiceDiscoveryApplication.java create mode 100644 spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/properties/MyProperties.java create mode 100644 spring-cloud/spring-cloud-consul/src/main/resources/application.yml create mode 100644 spring-cloud/spring-cloud-consul/src/main/resources/bootstrap.yml create mode 100644 undertow/dependency-reduced-pom.xml create mode 100644 xml/src/test/resources/example_dom4j_new.xml create mode 100644 xml/src/test/resources/example_jaxb_new.xml diff --git a/bootique/dependency-reduced-pom.xml b/bootique/dependency-reduced-pom.xml index ed18f4e42a..ab09cfb7b1 100644 --- a/bootique/dependency-reduced-pom.xml +++ b/bootique/dependency-reduced-pom.xml @@ -28,8 +28,14 @@ junit junit - 3.8.1 + 4.12 test + + + hamcrest-core + org.hamcrest + + diff --git a/libraries-data/file.dat b/libraries-data/file.dat new file mode 100644 index 0000000000000000000000000000000000000000..70177cef976ad64b5517006b8abb7acc41f58b6e GIT binary patch literal 87 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 AsQ>@~ literal 0 HcmV?d00001 diff --git a/spring-cloud/spring-cloud-consul/pom.xml b/spring-cloud/spring-cloud-consul/pom.xml new file mode 100644 index 0000000000..0a0650ec8b --- /dev/null +++ b/spring-cloud/spring-cloud-consul/pom.xml @@ -0,0 +1,58 @@ + + + 4.0.0 + + org.baeldung + spring-cloud-consul + jar + + spring-cloud-consul + + + com.baeldung.spring.cloud + spring-cloud + 1.0.0-SNAPSHOT + + + + UTF-8 + 3.6.0 + + + + + org.springframework.cloud + spring-cloud-starter-consul-all + 1.3.0.RELEASE + + + + org.springframework.cloud + spring-cloud-starter-consul-config + 1.3.0.RELEASE + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + 1.8 + 1.8 + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot-maven-plugin.version} + + + + + diff --git a/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/DiscoveryClientApplication.java b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/DiscoveryClientApplication.java new file mode 100644 index 0000000000..e5e18d71ff --- /dev/null +++ b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/DiscoveryClientApplication.java @@ -0,0 +1,53 @@ +package com.baeldung.spring.cloud.consul; + +import java.net.URI; +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.context.annotation.Bean; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; + +@SpringBootApplication +@EnableDiscoveryClient +@RestController +public class DiscoveryClientApplication { + + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } + + @Autowired + private RestTemplate restTemplate; + + @Autowired + private DiscoveryClient discoveryClient; + + @RequestMapping("/discoveryClient") + public String home() { + return this.restTemplate.getForEntity(serviceUrl().resolve("/ping"), String.class) + .getBody(); + } + + public URI serviceUrl() { + List list = discoveryClient.getInstances("myApp"); + if (list != null && list.size() > 0) { + return list.get(0) + .getUri(); + } + + return null; + } + + public static void main(String[] args) { + new SpringApplicationBuilder(DiscoveryClientApplication.class).web(true) + .run(args); + } +} diff --git a/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/DistributedPropertiesApplication.java b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/DistributedPropertiesApplication.java new file mode 100644 index 0000000000..12ecd96b26 --- /dev/null +++ b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/DistributedPropertiesApplication.java @@ -0,0 +1,36 @@ +package com.baeldung.spring.cloud.consul; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.baeldung.spring.cloud.consul.properties.MyProperties; + +@SpringBootApplication +@RestController +public class DistributedPropertiesApplication { + + @Value("${my.prop}") + String value; + + @Autowired + private MyProperties properties; + + @RequestMapping("/getConfigFromValue") + public String getConfigFromValue() { + return value; + } + + @RequestMapping("/getConfigFromProperty") + public String getConfigFromProperty() { + return properties.getProp(); + } + + public static void main(String[] args) { + new SpringApplicationBuilder(DistributedPropertiesApplication.class).web(true) + .run(args); + } +} diff --git a/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/RibbonClientApplication.java b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/RibbonClientApplication.java new file mode 100644 index 0000000000..620c3553ba --- /dev/null +++ b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/RibbonClientApplication.java @@ -0,0 +1,34 @@ +package com.baeldung.spring.cloud.consul; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.cloud.client.loadbalancer.LoadBalanced; +import org.springframework.context.annotation.Bean; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; + +@SpringBootApplication +@RestController +public class RibbonClientApplication { + + @LoadBalanced + @Bean + public RestTemplate loadbalancedRestTemplate() { + return new RestTemplate(); + } + + @Autowired + RestTemplate restTemplate; + + @RequestMapping("/ribbonClient") + public String home() { + return this.restTemplate.getForObject("http://myApp/ping", String.class); + } + + public static void main(String[] args) { + new SpringApplicationBuilder(RibbonClientApplication.class).web(true) + .run(args); + } +} diff --git a/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/ServiceDiscoveryApplication.java b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/ServiceDiscoveryApplication.java new file mode 100644 index 0000000000..5da8105d01 --- /dev/null +++ b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/ServiceDiscoveryApplication.java @@ -0,0 +1,29 @@ +package com.baeldung.spring.cloud.consul; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@SpringBootApplication +@RestController +public class ServiceDiscoveryApplication { + @RequestMapping("/my-health-check") + public ResponseEntity myCustomCheck() { + String message = "Testing my healh check function"; + return new ResponseEntity<>(message, HttpStatus.FORBIDDEN); + } + + @RequestMapping("/ping") + public String ping() { + return "pong"; + } + + public static void main(String[] args) { + new SpringApplicationBuilder(ServiceDiscoveryApplication.class).web(true) + .run(args); + } + +} \ No newline at end of file diff --git a/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/properties/MyProperties.java b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/properties/MyProperties.java new file mode 100644 index 0000000000..7accac168d --- /dev/null +++ b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/properties/MyProperties.java @@ -0,0 +1,20 @@ +package com.baeldung.spring.cloud.consul.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.context.annotation.Configuration; + +@RefreshScope +@Configuration +@ConfigurationProperties("my") +public class MyProperties { + private String prop; + + public String getProp() { + return prop; + } + + public void setProp(String prop) { + this.prop = prop; + } +} diff --git a/spring-cloud/spring-cloud-consul/src/main/resources/application.yml b/spring-cloud/spring-cloud-consul/src/main/resources/application.yml new file mode 100644 index 0000000000..ccdc3fdeee --- /dev/null +++ b/spring-cloud/spring-cloud-consul/src/main/resources/application.yml @@ -0,0 +1,14 @@ +spring: + application: + name: myApp + cloud: + consul: + host: localhost + port: 8500 + discovery: + healthCheckPath: /my-health-check + healthCheckInterval: 20s + enabled: true + instanceId: ${spring.application.name}:${random.value} +server: + port: 0 \ No newline at end of file diff --git a/spring-cloud/spring-cloud-consul/src/main/resources/bootstrap.yml b/spring-cloud/spring-cloud-consul/src/main/resources/bootstrap.yml new file mode 100644 index 0000000000..6318170135 --- /dev/null +++ b/spring-cloud/spring-cloud-consul/src/main/resources/bootstrap.yml @@ -0,0 +1,9 @@ +spring: + application: + name: myApp + cloud: + consul: + host: localhost + port: 8500 + config: + enabled: true \ No newline at end of file diff --git a/undertow/dependency-reduced-pom.xml b/undertow/dependency-reduced-pom.xml new file mode 100644 index 0000000000..f3bb8b78ed --- /dev/null +++ b/undertow/dependency-reduced-pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + com.baeldung.undertow + undertow + undertow + 1.0-SNAPSHOT + http://maven.apache.org + + ${project.artifactId} + + + maven-shade-plugin + + + package + + shade + + + + + + maven-jar-plugin + + + + com.baeldung.undertow.SimpleServer + + + + + + + + 1.8 + 1.8 + + + diff --git a/xml/src/test/resources/example_dom4j_new.xml b/xml/src/test/resources/example_dom4j_new.xml new file mode 100644 index 0000000000..020760fdd3 --- /dev/null +++ b/xml/src/test/resources/example_dom4j_new.xml @@ -0,0 +1,10 @@ + + + + + XML with Dom4J + XML handling with Dom4J + 14/06/2016 + Dom4J tech writer + + diff --git a/xml/src/test/resources/example_jaxb_new.xml b/xml/src/test/resources/example_jaxb_new.xml new file mode 100644 index 0000000000..646d938869 --- /dev/null +++ b/xml/src/test/resources/example_jaxb_new.xml @@ -0,0 +1,9 @@ + + + + Jaxb author + 04/02/2015 + XML Binding with Jaxb + XML with Jaxb + + From d51b1b4f20b98fbc30a42477103ab8b477872faa Mon Sep 17 00:00:00 2001 From: parthkaria Date: Fri, 1 Dec 2017 10:51:33 +0530 Subject: [PATCH 21/50] BAEL-21 Java 9 HttpClient code refactoring --- core-java-9/compile-httpclient.bat | 3 +++ core-java-9/src/main/java/module-info.java | 3 --- .../baeldung}/httpclient/HttpClientExample.java | 17 ++++++++++++----- .../com.baeldung.httpclient/module-info.java | 3 +++ 4 files changed, 18 insertions(+), 8 deletions(-) create mode 100644 core-java-9/compile-httpclient.bat delete mode 100644 core-java-9/src/main/java/module-info.java rename core-java-9/src/{main/java/com/baeldung/java9 => modules/com.baeldung.httpclient/com/baeldung}/httpclient/HttpClientExample.java (82%) create mode 100644 core-java-9/src/modules/com.baeldung.httpclient/module-info.java diff --git a/core-java-9/compile-httpclient.bat b/core-java-9/compile-httpclient.bat new file mode 100644 index 0000000000..9d845784cf --- /dev/null +++ b/core-java-9/compile-httpclient.bat @@ -0,0 +1,3 @@ +javac --module-path mods -d mods/com.baeldung.httpclient^ + src/modules/com.baeldung.httpclient/module-info.java^ + src/modules/com.baeldung.httpclient/com/baeldung/httpclient/HttpClientExample.java \ No newline at end of file diff --git a/core-java-9/src/main/java/module-info.java b/core-java-9/src/main/java/module-info.java deleted file mode 100644 index 163dd4f5be..0000000000 --- a/core-java-9/src/main/java/module-info.java +++ /dev/null @@ -1,3 +0,0 @@ -module com.baeldung.java9.httpclient { - requires jdk.incubator.httpclient; -} \ No newline at end of file diff --git a/core-java-9/src/main/java/com/baeldung/java9/httpclient/HttpClientExample.java b/core-java-9/src/modules/com.baeldung.httpclient/com/baeldung/httpclient/HttpClientExample.java similarity index 82% rename from core-java-9/src/main/java/com/baeldung/java9/httpclient/HttpClientExample.java rename to core-java-9/src/modules/com.baeldung.httpclient/com/baeldung/httpclient/HttpClientExample.java index 6ccfd030a5..44c55715c7 100644 --- a/core-java-9/src/main/java/com/baeldung/java9/httpclient/HttpClientExample.java +++ b/core-java-9/src/modules/com.baeldung.httpclient/com/baeldung/httpclient/HttpClientExample.java @@ -3,7 +3,7 @@ * To change this template file, choose Tools | Templates * and open the template in the editor. */ -package com.baeldung.java9.httpclient; +package com.baeldung.httpclient; import java.io.File; import java.io.IOException; @@ -25,8 +25,15 @@ import jdk.incubator.http.HttpResponse.BodyHandler; * @author pkaria */ public class HttpClientExample { + + public static void main(String[] args) throws Exception { + httpGetRequest(); + httpPosttRequest(); + asynchronousRequest(); + asynchronousMultipleRequests(); + } - public void httpGetRequest() throws URISyntaxException, IOException, InterruptedException { + public static void httpGetRequest() throws URISyntaxException, IOException, InterruptedException { HttpClient client = HttpClient.newHttpClient(); URI httpURI = new URI("http://jsonplaceholder.typicode.com/posts/1"); HttpRequest request = HttpRequest.newBuilder(httpURI).GET().build(); @@ -36,7 +43,7 @@ public class HttpClientExample { System.out.println(responseBody); } - public void httpPosttRequest() throws URISyntaxException, IOException, InterruptedException { + public static void httpPosttRequest() throws URISyntaxException, IOException, InterruptedException { HttpClient client = HttpClient .newBuilder() .build(); @@ -50,7 +57,7 @@ public class HttpClientExample { System.out.println(responseBody); } - public void asynchronousRequest() throws URISyntaxException { + public static void asynchronousRequest() throws URISyntaxException { HttpClient client = HttpClient.newHttpClient(); URI httpURI = new URI("http://jsonplaceholder.typicode.com/posts/1"); HttpRequest request = HttpRequest.newBuilder(httpURI).GET().build(); @@ -58,7 +65,7 @@ public class HttpClientExample { HttpResponse.BodyHandler.asString()); } - public void asynchronousMultipleRequests() throws URISyntaxException { + public static void asynchronousMultipleRequests() throws URISyntaxException { List targets = Arrays.asList(new URI("http://jsonplaceholder.typicode.com/posts/1"), new URI("http://jsonplaceholder.typicode.com/posts/2")); HttpClient client = HttpClient.newHttpClient(); List> futures = targets diff --git a/core-java-9/src/modules/com.baeldung.httpclient/module-info.java b/core-java-9/src/modules/com.baeldung.httpclient/module-info.java new file mode 100644 index 0000000000..205c9ea725 --- /dev/null +++ b/core-java-9/src/modules/com.baeldung.httpclient/module-info.java @@ -0,0 +1,3 @@ +module com.baeldung.httpclient { + requires jdk.incubator.httpclient; +} From 42c894e7f96748c25427fc73e13372c6a2a06d4b Mon Sep 17 00:00:00 2001 From: Ahmed Tawila Date: Fri, 1 Dec 2017 08:37:19 +0200 Subject: [PATCH 22/50] BAEL-1307 Java Nested Classes (#3157) * Evaluation article: Different Types of Bean Injection in Spring * added tests & changed configuration to Java-based config * removed xml config files * rename unit tests * BAEL-972 - Apache Commons Text * remove code from evaluation article * remove code from evaluation article * BAEL-972 - Apache Commons Text - added another example * BAEL-972 - Apache Commons Text - just indentation * BAEL-994 - TemporalAdjuster in Java * BAEL-994 - TemporalAdjuster in Java * BAEL-994 - TemporalAdjuster in Java * BAEL-994 - TemporalAdjuster in Java * BAEL-994 - TemporalAdjuster in Java - fix problems * BAEL-1033 Introduction to StreamUtils * BAEL-1033 Introduction to StreamUtils * BAEL-1033 Introduction to StreamUtils * fix formatting * BAEL-1033 minor refactor * BAEL-1035 Introduction to Eclipse Collections * format * BAEL-1035 Introduction to Eclipse Collections * BAEL-1035 Introduction to Eclipse Collections * BAEL-1035 Introduction to Eclipse Collections * cleanup * cleanup * BAEL-1109 Introduction to JCache * BAEL-1109 Introduction to JCache * remove unneeded property in pom.xml * fix formatting * close cache instances properly * remove latest commit * BAEL-1057 Introduction to rxjava-jdbc * refactor rxjava-jdbc * Refactor rxjava-jdbc * Refactoring rxjava-jdbc * BAEL-1171 java.lang.String API * refactor rxjava-jdbc * refactor String * String API - move multiple classes into a single class * move class into test package * BAEL-1171 String.lang.String API * BAEL-1171 java.lang.String API * BAEL-1250 Initializing Arrays in Java * BAEL-1250 Initializing Arrays in Java * BAEL-1250 Initializing Arrays in Java * small fix * BAEL-1171 java.lang.String API * BAEL-1263 Daemon Threads in Java * merge with fork * BAEL-1263 Daemon Threads in Java * fix compilation error * BAEL-1309 Append Data to a File with Java * fix build fails * fix test error * fix build failures * cleanup * fix Sneaky Runnable test * revert sneaky runnable fix attempt * fix test failure * fix test failure * fix all test failures * fix test failure * fix test failures * Nested Classes in Java * enhance nested classes --- .../com/baeldung/nestedclass/Enclosing.java | 11 ------- .../java/com/baeldung/nestedclass/Outer.java | 11 ------- .../nestedclass/SimpleAbstractClass.java | 5 --- ...mousInnerTest.java => AnonymousInner.java} | 8 +++-- .../com/baeldung/nestedclass/Enclosing.java | 21 ++++++++++++ .../baeldung/nestedclass/InnerClassTest.java | 13 -------- .../baeldung/nestedclass/LocalClassTest.java | 12 ------- .../baeldung/nestedclass/NestedClassTest.java | 12 ------- .../baeldung/nestedclass/NewEnclosing.java | 11 +++++-- .../com/baeldung/nestedclass/NewOuter.java | 30 +++++++++++++++++ .../baeldung/nestedclass/NewOuterTest.java | 32 ------------------- .../java/com/baeldung/nestedclass/Outer.java | 20 ++++++++++++ 12 files changed, 86 insertions(+), 100 deletions(-) delete mode 100644 core-java/src/main/java/com/baeldung/nestedclass/Enclosing.java delete mode 100644 core-java/src/main/java/com/baeldung/nestedclass/Outer.java delete mode 100644 core-java/src/main/java/com/baeldung/nestedclass/SimpleAbstractClass.java rename core-java/src/test/java/com/baeldung/nestedclass/{AnonymousInnerTest.java => AnonymousInner.java} (72%) create mode 100644 core-java/src/test/java/com/baeldung/nestedclass/Enclosing.java delete mode 100644 core-java/src/test/java/com/baeldung/nestedclass/InnerClassTest.java delete mode 100644 core-java/src/test/java/com/baeldung/nestedclass/LocalClassTest.java delete mode 100644 core-java/src/test/java/com/baeldung/nestedclass/NestedClassTest.java rename core-java/src/{main => test}/java/com/baeldung/nestedclass/NewEnclosing.java (59%) create mode 100644 core-java/src/test/java/com/baeldung/nestedclass/NewOuter.java delete mode 100644 core-java/src/test/java/com/baeldung/nestedclass/NewOuterTest.java create mode 100644 core-java/src/test/java/com/baeldung/nestedclass/Outer.java diff --git a/core-java/src/main/java/com/baeldung/nestedclass/Enclosing.java b/core-java/src/main/java/com/baeldung/nestedclass/Enclosing.java deleted file mode 100644 index a9911538b0..0000000000 --- a/core-java/src/main/java/com/baeldung/nestedclass/Enclosing.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.baeldung.nestedclass; - -public class Enclosing { - - public static class Nested { - - public void test() { - System.out.println("Calling test..."); - } - } -} diff --git a/core-java/src/main/java/com/baeldung/nestedclass/Outer.java b/core-java/src/main/java/com/baeldung/nestedclass/Outer.java deleted file mode 100644 index ebd6d27293..0000000000 --- a/core-java/src/main/java/com/baeldung/nestedclass/Outer.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.baeldung.nestedclass; - -public class Outer { - - public class Inner { - - public void test() { - System.out.println("Calling test..."); - } - } -} diff --git a/core-java/src/main/java/com/baeldung/nestedclass/SimpleAbstractClass.java b/core-java/src/main/java/com/baeldung/nestedclass/SimpleAbstractClass.java deleted file mode 100644 index 586e2d12b4..0000000000 --- a/core-java/src/main/java/com/baeldung/nestedclass/SimpleAbstractClass.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.baeldung.nestedclass; - -abstract class SimpleAbstractClass { - abstract void run(); -} \ No newline at end of file diff --git a/core-java/src/test/java/com/baeldung/nestedclass/AnonymousInnerTest.java b/core-java/src/test/java/com/baeldung/nestedclass/AnonymousInner.java similarity index 72% rename from core-java/src/test/java/com/baeldung/nestedclass/AnonymousInnerTest.java rename to core-java/src/test/java/com/baeldung/nestedclass/AnonymousInner.java index 394c0bb57a..9fa8ee9cd5 100644 --- a/core-java/src/test/java/com/baeldung/nestedclass/AnonymousInnerTest.java +++ b/core-java/src/test/java/com/baeldung/nestedclass/AnonymousInner.java @@ -2,10 +2,14 @@ package com.baeldung.nestedclass; import org.junit.Test; -public class AnonymousInnerTest { +abstract class SimpleAbstractClass { + abstract void run(); +} + +public class AnonymousInner { @Test - public void whenRunAnonymousClass_thenCorrect() { + public void run() { SimpleAbstractClass simpleAbstractClass = new SimpleAbstractClass() { void run() { System.out.println("Running Anonymous Class..."); diff --git a/core-java/src/test/java/com/baeldung/nestedclass/Enclosing.java b/core-java/src/test/java/com/baeldung/nestedclass/Enclosing.java new file mode 100644 index 0000000000..3db33cde9b --- /dev/null +++ b/core-java/src/test/java/com/baeldung/nestedclass/Enclosing.java @@ -0,0 +1,21 @@ +package com.baeldung.nestedclass; + +import org.junit.Test; + +public class Enclosing { + + private static int x = 1; + + public static class StaticNested { + + private void run() { + System.out.println("x = " + x); + } + } + + @Test + public void test() { + Enclosing.StaticNested nested = new Enclosing.StaticNested(); + nested.run(); + } +} diff --git a/core-java/src/test/java/com/baeldung/nestedclass/InnerClassTest.java b/core-java/src/test/java/com/baeldung/nestedclass/InnerClassTest.java deleted file mode 100644 index e9cb119ac2..0000000000 --- a/core-java/src/test/java/com/baeldung/nestedclass/InnerClassTest.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.baeldung.nestedclass; - -import org.junit.Test; - -public class InnerClassTest { - - @Test - public void givenInnerClassWhenInstantiating_thenCorrect() { - Outer outer = new Outer(); - Outer.Inner inner = outer.new Inner(); - inner.test(); - } -} diff --git a/core-java/src/test/java/com/baeldung/nestedclass/LocalClassTest.java b/core-java/src/test/java/com/baeldung/nestedclass/LocalClassTest.java deleted file mode 100644 index dad19161ad..0000000000 --- a/core-java/src/test/java/com/baeldung/nestedclass/LocalClassTest.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.baeldung.nestedclass; - -import org.junit.Test; - -public class LocalClassTest { - - @Test - public void whenTestingLocalClass_thenCorrect() { - NewEnclosing newEnclosing = new NewEnclosing(); - newEnclosing.run(); - } -} diff --git a/core-java/src/test/java/com/baeldung/nestedclass/NestedClassTest.java b/core-java/src/test/java/com/baeldung/nestedclass/NestedClassTest.java deleted file mode 100644 index 16c883689a..0000000000 --- a/core-java/src/test/java/com/baeldung/nestedclass/NestedClassTest.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.baeldung.nestedclass; - -import org.junit.Test; - -public class NestedClassTest { - - @Test - public void whenInstantiatingStaticNestedClass_thenCorrect() { - Enclosing.Nested nested = new Enclosing.Nested(); - nested.test(); - } -} diff --git a/core-java/src/main/java/com/baeldung/nestedclass/NewEnclosing.java b/core-java/src/test/java/com/baeldung/nestedclass/NewEnclosing.java similarity index 59% rename from core-java/src/main/java/com/baeldung/nestedclass/NewEnclosing.java rename to core-java/src/test/java/com/baeldung/nestedclass/NewEnclosing.java index c7e04e8600..deeb72de0c 100644 --- a/core-java/src/main/java/com/baeldung/nestedclass/NewEnclosing.java +++ b/core-java/src/test/java/com/baeldung/nestedclass/NewEnclosing.java @@ -1,10 +1,11 @@ package com.baeldung.nestedclass; +import org.junit.Test; + public class NewEnclosing { - void run() { + private void run() { class Local { - void run() { System.out.println("Welcome to Baeldung!"); } @@ -12,4 +13,10 @@ public class NewEnclosing { Local local = new Local(); local.run(); } + + @Test + public void test() { + NewEnclosing newEnclosing = new NewEnclosing(); + newEnclosing.run(); + } } \ No newline at end of file diff --git a/core-java/src/test/java/com/baeldung/nestedclass/NewOuter.java b/core-java/src/test/java/com/baeldung/nestedclass/NewOuter.java new file mode 100644 index 0000000000..a3a723b30e --- /dev/null +++ b/core-java/src/test/java/com/baeldung/nestedclass/NewOuter.java @@ -0,0 +1,30 @@ +package com.baeldung.nestedclass; + +import org.junit.Test; + +public class NewOuter { + + int a = 1; + static int b = 2; + + public class InnerClass { + int a = 3; + static final int b = 4; + + public void run() { + System.out.println("a = " + a); + System.out.println("b = " + b); + System.out.println("NewOuterTest.this.a = " + NewOuter.this.a); + System.out.println("NewOuterTest.b = " + NewOuter.b); + System.out.println("NewOuterTest.this.b = " + NewOuter.this.b); + } + } + + @Test + public void test() { + NewOuter outer = new NewOuter(); + NewOuter.InnerClass inner = outer.new InnerClass(); + inner.run(); + + } +} \ No newline at end of file diff --git a/core-java/src/test/java/com/baeldung/nestedclass/NewOuterTest.java b/core-java/src/test/java/com/baeldung/nestedclass/NewOuterTest.java deleted file mode 100644 index e883687d33..0000000000 --- a/core-java/src/test/java/com/baeldung/nestedclass/NewOuterTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.baeldung.nestedclass; - -import static org.junit.Assert.assertEquals; -import org.junit.Test; - -public class NewOuterTest { - - int a = 1; - static int b = 2; - - public class InnerClass { - int a = 3; - static final int b = 4; - - @Test - public void whenShadowing_thenCorrect() { - assertEquals(3, a); - assertEquals(4, b); - assertEquals(1, NewOuterTest.this.a); - assertEquals(2, NewOuterTest.b); - assertEquals(2, NewOuterTest.this.b); - } - } - - @Test - public void shadowingTest() { - NewOuterTest outer = new NewOuterTest(); - NewOuterTest.InnerClass inner = outer.new InnerClass(); - inner.whenShadowing_thenCorrect(); - - } -} \ No newline at end of file diff --git a/core-java/src/test/java/com/baeldung/nestedclass/Outer.java b/core-java/src/test/java/com/baeldung/nestedclass/Outer.java new file mode 100644 index 0000000000..d5e46670c9 --- /dev/null +++ b/core-java/src/test/java/com/baeldung/nestedclass/Outer.java @@ -0,0 +1,20 @@ +package com.baeldung.nestedclass; + +import org.junit.Test; + +public class Outer { + + public class Inner { + + public void run() { + System.out.println("Calling test..."); + } + } + + @Test + public void test() { + Outer outer = new Outer(); + Outer.Inner inner = outer.new Inner(); + inner.run(); + } +} From f84dbbbb45aa29caddef58b77de2388a90abd302 Mon Sep 17 00:00:00 2001 From: ryan Date: Fri, 1 Dec 2017 14:55:04 +0800 Subject: [PATCH 23/50] fix parent.relativePath config for maven submodules in separate folders (#3148) --- guava-modules/guava-18/pom.xml | 2 +- guava-modules/guava-19/pom.xml | 2 +- guava-modules/guava-21/pom.xml | 2 +- logging-modules/log-mdc/pom.xml | 2 +- logging-modules/log4j/pom.xml | 2 +- logging-modules/log4j2/pom.xml | 2 +- persistence-modules/java-cassandra/pom.xml | 2 +- persistence-modules/java-mongodb/pom.xml | 2 +- persistence-modules/liquibase/pom.xml | 2 +- persistence-modules/querydsl/pom.xml | 2 +- persistence-modules/redis/pom.xml | 2 +- persistence-modules/solr/pom.xml | 2 +- persistence-modules/spring-data-cassandra/pom.xml | 2 +- persistence-modules/spring-data-gemfire/pom.xml | 2 +- persistence-modules/spring-data-neo4j/pom.xml | 2 +- persistence-modules/spring-data-redis/pom.xml | 2 +- persistence-modules/spring-data-solr/pom.xml | 2 +- persistence-modules/spring-hibernate-3/pom.xml | 2 +- persistence-modules/spring-hibernate-5/pom.xml | 2 +- testing-modules/groovy-spock/pom.xml | 2 +- testing-modules/junit-5/pom.xml | 2 +- testing-modules/mockito-2/pom.xml | 2 +- testing-modules/mockito/pom.xml | 2 +- testing-modules/mocks/pom.xml | 2 +- testing-modules/rest-assured/pom.xml | 2 +- testing-modules/rest-testing/pom.xml | 2 +- testing-modules/selenium-junit-testng/pom.xml | 2 +- testing-modules/testing/pom.xml | 2 +- testing-modules/testng/pom.xml | 2 +- 29 files changed, 29 insertions(+), 29 deletions(-) diff --git a/guava-modules/guava-18/pom.xml b/guava-modules/guava-18/pom.xml index a9aba47f12..f8dbf5657e 100644 --- a/guava-modules/guava-18/pom.xml +++ b/guava-modules/guava-18/pom.xml @@ -11,7 +11,7 @@ com.baeldung parent-modules 1.0.0-SNAPSHOT - ../ + ../../ diff --git a/guava-modules/guava-19/pom.xml b/guava-modules/guava-19/pom.xml index 2345212eba..4a23bf7aec 100644 --- a/guava-modules/guava-19/pom.xml +++ b/guava-modules/guava-19/pom.xml @@ -11,7 +11,7 @@ com.baeldung parent-modules 1.0.0-SNAPSHOT - ../ + ../../ diff --git a/guava-modules/guava-21/pom.xml b/guava-modules/guava-21/pom.xml index 94bb66e76a..f5432fb7df 100644 --- a/guava-modules/guava-21/pom.xml +++ b/guava-modules/guava-21/pom.xml @@ -11,7 +11,7 @@ com.baeldung parent-modules 1.0.0-SNAPSHOT - ../ + ../../ diff --git a/logging-modules/log-mdc/pom.xml b/logging-modules/log-mdc/pom.xml index 918e052a15..5551585372 100644 --- a/logging-modules/log-mdc/pom.xml +++ b/logging-modules/log-mdc/pom.xml @@ -12,7 +12,7 @@ com.baeldung parent-modules 1.0.0-SNAPSHOT - ../ + ../../ diff --git a/logging-modules/log4j/pom.xml b/logging-modules/log4j/pom.xml index a3bfb0a33a..6a3fbde393 100644 --- a/logging-modules/log4j/pom.xml +++ b/logging-modules/log4j/pom.xml @@ -11,7 +11,7 @@ com.baeldung parent-modules 1.0.0-SNAPSHOT - ../ + ../../ diff --git a/logging-modules/log4j2/pom.xml b/logging-modules/log4j2/pom.xml index 58bc4b74e7..48608fbc80 100644 --- a/logging-modules/log4j2/pom.xml +++ b/logging-modules/log4j2/pom.xml @@ -9,7 +9,7 @@ com.baeldung parent-modules 1.0.0-SNAPSHOT - ../ + ../../ diff --git a/persistence-modules/java-cassandra/pom.xml b/persistence-modules/java-cassandra/pom.xml index faaabb9e2e..81e072c3a7 100644 --- a/persistence-modules/java-cassandra/pom.xml +++ b/persistence-modules/java-cassandra/pom.xml @@ -11,7 +11,7 @@ com.baeldung parent-modules 1.0.0-SNAPSHOT - ../ + ../../ diff --git a/persistence-modules/java-mongodb/pom.xml b/persistence-modules/java-mongodb/pom.xml index aab48921a6..9784b2c5a8 100644 --- a/persistence-modules/java-mongodb/pom.xml +++ b/persistence-modules/java-mongodb/pom.xml @@ -11,7 +11,7 @@ com.baeldung parent-modules 1.0.0-SNAPSHOT - ../ + ../../ diff --git a/persistence-modules/liquibase/pom.xml b/persistence-modules/liquibase/pom.xml index 020c2516a2..a574ba497c 100644 --- a/persistence-modules/liquibase/pom.xml +++ b/persistence-modules/liquibase/pom.xml @@ -6,7 +6,7 @@ parent-modules com.baeldung 1.0.0-SNAPSHOT - ../ + ../../ 4.0.0 diff --git a/persistence-modules/querydsl/pom.xml b/persistence-modules/querydsl/pom.xml index 27f383e0c6..c2943875f1 100644 --- a/persistence-modules/querydsl/pom.xml +++ b/persistence-modules/querydsl/pom.xml @@ -15,7 +15,7 @@ com.baeldung parent-modules 1.0.0-SNAPSHOT - ../ + ../../ diff --git a/persistence-modules/redis/pom.xml b/persistence-modules/redis/pom.xml index ef081a2c69..4321b491eb 100644 --- a/persistence-modules/redis/pom.xml +++ b/persistence-modules/redis/pom.xml @@ -15,7 +15,7 @@ com.baeldung parent-modules 1.0.0-SNAPSHOT - ../ + ../../ diff --git a/persistence-modules/solr/pom.xml b/persistence-modules/solr/pom.xml index 2fd0bdd721..966bd8755b 100644 --- a/persistence-modules/solr/pom.xml +++ b/persistence-modules/solr/pom.xml @@ -12,7 +12,7 @@ com.baeldung parent-modules 1.0.0-SNAPSHOT - ../ + ../../ diff --git a/persistence-modules/spring-data-cassandra/pom.xml b/persistence-modules/spring-data-cassandra/pom.xml index 607d7b90ba..1358210a45 100644 --- a/persistence-modules/spring-data-cassandra/pom.xml +++ b/persistence-modules/spring-data-cassandra/pom.xml @@ -13,7 +13,7 @@ com.baeldung parent-modules 1.0.0-SNAPSHOT - ../ + ../../ diff --git a/persistence-modules/spring-data-gemfire/pom.xml b/persistence-modules/spring-data-gemfire/pom.xml index 9108865b4c..3f7fcd03e5 100644 --- a/persistence-modules/spring-data-gemfire/pom.xml +++ b/persistence-modules/spring-data-gemfire/pom.xml @@ -12,7 +12,7 @@ com.baeldung parent-modules 1.0.0-SNAPSHOT - ../ + ../../ diff --git a/persistence-modules/spring-data-neo4j/pom.xml b/persistence-modules/spring-data-neo4j/pom.xml index 0055850ec3..bdd51e9659 100644 --- a/persistence-modules/spring-data-neo4j/pom.xml +++ b/persistence-modules/spring-data-neo4j/pom.xml @@ -10,7 +10,7 @@ com.baeldung parent-modules 1.0.0-SNAPSHOT - ../ + ../../ diff --git a/persistence-modules/spring-data-redis/pom.xml b/persistence-modules/spring-data-redis/pom.xml index b184d7e369..6cb49f11cf 100644 --- a/persistence-modules/spring-data-redis/pom.xml +++ b/persistence-modules/spring-data-redis/pom.xml @@ -11,7 +11,7 @@ com.baeldung parent-modules 1.0.0-SNAPSHOT - ../ + ../../ diff --git a/persistence-modules/spring-data-solr/pom.xml b/persistence-modules/spring-data-solr/pom.xml index 0759c1dbc0..e24d8314ba 100644 --- a/persistence-modules/spring-data-solr/pom.xml +++ b/persistence-modules/spring-data-solr/pom.xml @@ -12,7 +12,7 @@ com.baeldung parent-modules 1.0.0-SNAPSHOT - ../ + ../../ diff --git a/persistence-modules/spring-hibernate-3/pom.xml b/persistence-modules/spring-hibernate-3/pom.xml index 8eee819572..f1873a84d3 100644 --- a/persistence-modules/spring-hibernate-3/pom.xml +++ b/persistence-modules/spring-hibernate-3/pom.xml @@ -11,7 +11,7 @@ com.baeldung parent-modules 1.0.0-SNAPSHOT - ../ + ../../ diff --git a/persistence-modules/spring-hibernate-5/pom.xml b/persistence-modules/spring-hibernate-5/pom.xml index f1f3d10347..57eb1aeffb 100644 --- a/persistence-modules/spring-hibernate-5/pom.xml +++ b/persistence-modules/spring-hibernate-5/pom.xml @@ -11,7 +11,7 @@ com.baeldung parent-modules 1.0.0-SNAPSHOT - ../ + ../../ diff --git a/testing-modules/groovy-spock/pom.xml b/testing-modules/groovy-spock/pom.xml index f4e61e6786..3d67657224 100644 --- a/testing-modules/groovy-spock/pom.xml +++ b/testing-modules/groovy-spock/pom.xml @@ -17,7 +17,7 @@ com.baeldung parent-modules 1.0.0-SNAPSHOT - ../ + ../../ diff --git a/testing-modules/junit-5/pom.xml b/testing-modules/junit-5/pom.xml index 2be8937d04..684a9253d5 100644 --- a/testing-modules/junit-5/pom.xml +++ b/testing-modules/junit-5/pom.xml @@ -14,7 +14,7 @@ com.baeldung parent-modules 1.0.0-SNAPSHOT - ../ + ../../ diff --git a/testing-modules/mockito-2/pom.xml b/testing-modules/mockito-2/pom.xml index 0291ac4ec1..2d119ae8af 100644 --- a/testing-modules/mockito-2/pom.xml +++ b/testing-modules/mockito-2/pom.xml @@ -12,7 +12,7 @@ com.baeldung parent-modules 1.0.0-SNAPSHOT - ../ + ../../ diff --git a/testing-modules/mockito/pom.xml b/testing-modules/mockito/pom.xml index aa3dd9b20a..0e83c46926 100644 --- a/testing-modules/mockito/pom.xml +++ b/testing-modules/mockito/pom.xml @@ -11,7 +11,7 @@ com.baeldung parent-modules 1.0.0-SNAPSHOT - ../ + ../../ diff --git a/testing-modules/mocks/pom.xml b/testing-modules/mocks/pom.xml index 959c1851d6..6473f07c13 100644 --- a/testing-modules/mocks/pom.xml +++ b/testing-modules/mocks/pom.xml @@ -6,7 +6,7 @@ com.baeldung parent-modules 1.0.0-SNAPSHOT - ../ + ../../ mocks diff --git a/testing-modules/rest-assured/pom.xml b/testing-modules/rest-assured/pom.xml index 0c0826c5c3..1006e9a373 100644 --- a/testing-modules/rest-assured/pom.xml +++ b/testing-modules/rest-assured/pom.xml @@ -10,7 +10,7 @@ com.baeldung parent-modules 1.0.0-SNAPSHOT - ../ + ../../ diff --git a/testing-modules/rest-testing/pom.xml b/testing-modules/rest-testing/pom.xml index 4b838720da..ea63ee0e58 100644 --- a/testing-modules/rest-testing/pom.xml +++ b/testing-modules/rest-testing/pom.xml @@ -11,7 +11,7 @@ com.baeldung parent-modules 1.0.0-SNAPSHOT - ../ + ../../ diff --git a/testing-modules/selenium-junit-testng/pom.xml b/testing-modules/selenium-junit-testng/pom.xml index 14169e5749..418dd495a4 100644 --- a/testing-modules/selenium-junit-testng/pom.xml +++ b/testing-modules/selenium-junit-testng/pom.xml @@ -9,7 +9,7 @@ com.baeldung parent-modules 1.0.0-SNAPSHOT - ../ + ../../ diff --git a/testing-modules/testing/pom.xml b/testing-modules/testing/pom.xml index 3ad503558f..7aff0a93e0 100644 --- a/testing-modules/testing/pom.xml +++ b/testing-modules/testing/pom.xml @@ -10,7 +10,7 @@ com.baeldung parent-modules 1.0.0-SNAPSHOT - ../ + ../../ diff --git a/testing-modules/testng/pom.xml b/testing-modules/testng/pom.xml index f7a50954fc..7aed1837e5 100644 --- a/testing-modules/testng/pom.xml +++ b/testing-modules/testng/pom.xml @@ -11,7 +11,7 @@ com.baeldung parent-modules 1.0.0-SNAPSHOT - ../ + ../../ From 2ad2d4d820a806d46bba6d1f9aebac7e618ed142 Mon Sep 17 00:00:00 2001 From: linhvovn Date: Fri, 1 Dec 2017 14:55:22 +0800 Subject: [PATCH 24/50] [tlinh2110@gmail.com]Bael1313 Create spring-acl module (#3131) * [tlinh2110@gmail.com] [BAEL1313] Introduction Spring ACL * [tlinh2110@gmail.com] BAEL 1313 - Update unit test * [tlinh2110@gmail.com]BAEL1313- remove unused files from spring-core * [BAEL1313] Create spring-acl module * [BAEL1313] Add resources for Spring ACL * [BAEL1313] Remove acl from spring-security-mvc-boot * [BAEL1313] Add Spring ACL module in parent pom.xml --- pom.xml | 1 + spring-acl/pom.xml | 66 ++++++++++ .../org/baeldung/acl/config/ACLContext.java | 80 ++++++++++++ .../AclMethodSecurityConfiguration.java | 21 ++++ .../acl/config/JPAPersistenceConfig.java | 16 +++ .../dao/NoticeMessageRepository.java | 24 ++++ .../acl/persistence/entity/NoticeMessage.java | 29 +++++ spring-acl/src/main/resources/acl-data.sql | 28 +++++ spring-acl/src/main/resources/acl-schema.sql | 58 +++++++++ .../org.baeldung.acl.datasource.properties | 12 ++ .../java/org/baeldung/acl/SpringAclTest.java | 119 ++++++++++++++++++ 11 files changed, 454 insertions(+) create mode 100644 spring-acl/pom.xml create mode 100644 spring-acl/src/main/java/org/baeldung/acl/config/ACLContext.java create mode 100644 spring-acl/src/main/java/org/baeldung/acl/config/AclMethodSecurityConfiguration.java create mode 100644 spring-acl/src/main/java/org/baeldung/acl/config/JPAPersistenceConfig.java create mode 100644 spring-acl/src/main/java/org/baeldung/acl/persistence/dao/NoticeMessageRepository.java create mode 100644 spring-acl/src/main/java/org/baeldung/acl/persistence/entity/NoticeMessage.java create mode 100644 spring-acl/src/main/resources/acl-data.sql create mode 100644 spring-acl/src/main/resources/acl-schema.sql create mode 100644 spring-acl/src/main/resources/org.baeldung.acl.datasource.properties create mode 100644 spring-acl/src/test/java/org/baeldung/acl/SpringAclTest.java diff --git a/pom.xml b/pom.xml index 9b1f50f05e..cac0cc5845 100644 --- a/pom.xml +++ b/pom.xml @@ -141,6 +141,7 @@ spark-java spring-5-mvc + spring-acl spring-activiti spring-akka spring-amqp diff --git a/spring-acl/pom.xml b/spring-acl/pom.xml new file mode 100644 index 0000000000..3bcc0cf596 --- /dev/null +++ b/spring-acl/pom.xml @@ -0,0 +1,66 @@ + + + 4.0.0 + + com.baeldung + spring-acl + 0.0.1-SNAPSHOT + war + + spring-acl + Spring ACL + + + parent-boot-5 + com.baeldung + 0.0.1-SNAPSHOT + ../parent-boot-5 + + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + com.h2database + h2 + + + + org.springframework + spring-test + test + + + + org.springframework.security + spring-security-test + test + + + + org.springframework.security + spring-security-acl + + + org.springframework.security + spring-security-config + + + org.springframework + spring-context-support + + + net.sf.ehcache + ehcache-core + 2.6.11 + jar + + + + + diff --git a/spring-acl/src/main/java/org/baeldung/acl/config/ACLContext.java b/spring-acl/src/main/java/org/baeldung/acl/config/ACLContext.java new file mode 100644 index 0000000000..63a4ea58ef --- /dev/null +++ b/spring-acl/src/main/java/org/baeldung/acl/config/ACLContext.java @@ -0,0 +1,80 @@ +package org.baeldung.acl.config; + +import javax.sql.DataSource; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.cache.ehcache.EhCacheFactoryBean; +import org.springframework.cache.ehcache.EhCacheManagerFactoryBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; +import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; +import org.springframework.security.acls.AclPermissionCacheOptimizer; +import org.springframework.security.acls.AclPermissionEvaluator; +import org.springframework.security.acls.domain.AclAuthorizationStrategy; +import org.springframework.security.acls.domain.AclAuthorizationStrategyImpl; +import org.springframework.security.acls.domain.ConsoleAuditLogger; +import org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy; +import org.springframework.security.acls.domain.EhCacheBasedAclCache; +import org.springframework.security.acls.jdbc.BasicLookupStrategy; +import org.springframework.security.acls.jdbc.JdbcMutableAclService; +import org.springframework.security.acls.jdbc.LookupStrategy; +import org.springframework.security.acls.model.PermissionGrantingStrategy; +import org.springframework.security.core.authority.SimpleGrantedAuthority; + +@Configuration +@EnableAutoConfiguration +public class ACLContext { + + @Autowired + DataSource dataSource; + + @Bean + public EhCacheBasedAclCache aclCache() { + return new EhCacheBasedAclCache(aclEhCacheFactoryBean().getObject(), permissionGrantingStrategy(), aclAuthorizationStrategy()); + } + + @Bean + public EhCacheFactoryBean aclEhCacheFactoryBean() { + EhCacheFactoryBean ehCacheFactoryBean = new EhCacheFactoryBean(); + ehCacheFactoryBean.setCacheManager(aclCacheManager().getObject()); + ehCacheFactoryBean.setCacheName("aclCache"); + return ehCacheFactoryBean; + } + + @Bean + public EhCacheManagerFactoryBean aclCacheManager() { + return new EhCacheManagerFactoryBean(); + } + + @Bean + public PermissionGrantingStrategy permissionGrantingStrategy() { + return new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger()); + } + + @Bean + public AclAuthorizationStrategy aclAuthorizationStrategy() { + return new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_ADMIN")); + } + + @Bean + public MethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler() { + DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); + AclPermissionEvaluator permissionEvaluator = new AclPermissionEvaluator(aclService()); + expressionHandler.setPermissionEvaluator(permissionEvaluator); + expressionHandler.setPermissionCacheOptimizer(new AclPermissionCacheOptimizer(aclService())); + return expressionHandler; + } + + @Bean + public LookupStrategy lookupStrategy() { + return new BasicLookupStrategy(dataSource, aclCache(), aclAuthorizationStrategy(), new ConsoleAuditLogger()); + } + + @Bean + public JdbcMutableAclService aclService() { + return new JdbcMutableAclService(dataSource, lookupStrategy(), aclCache()); + } + +} \ No newline at end of file diff --git a/spring-acl/src/main/java/org/baeldung/acl/config/AclMethodSecurityConfiguration.java b/spring-acl/src/main/java/org/baeldung/acl/config/AclMethodSecurityConfiguration.java new file mode 100644 index 0000000000..110c4a6d24 --- /dev/null +++ b/spring-acl/src/main/java/org/baeldung/acl/config/AclMethodSecurityConfiguration.java @@ -0,0 +1,21 @@ +package org.baeldung.acl.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration; + +@Configuration +@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) +public class AclMethodSecurityConfiguration extends GlobalMethodSecurityConfiguration { + + @Autowired + MethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler; + + @Override + protected MethodSecurityExpressionHandler createExpressionHandler() { + return defaultMethodSecurityExpressionHandler; + } + +} diff --git a/spring-acl/src/main/java/org/baeldung/acl/config/JPAPersistenceConfig.java b/spring-acl/src/main/java/org/baeldung/acl/config/JPAPersistenceConfig.java new file mode 100644 index 0000000000..9b87efa92c --- /dev/null +++ b/spring-acl/src/main/java/org/baeldung/acl/config/JPAPersistenceConfig.java @@ -0,0 +1,16 @@ +package org.baeldung.acl.config; + +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +@Configuration +@EnableTransactionManagement +@EnableJpaRepositories(basePackages = "org.baeldung.acl.persistence.dao") +@PropertySource("classpath:org.baeldung.acl.datasource.properties") +@EntityScan(basePackages={ "org.baeldung.acl.persistence.entity" }) +public class JPAPersistenceConfig { + +} diff --git a/spring-acl/src/main/java/org/baeldung/acl/persistence/dao/NoticeMessageRepository.java b/spring-acl/src/main/java/org/baeldung/acl/persistence/dao/NoticeMessageRepository.java new file mode 100644 index 0000000000..8662c88d6c --- /dev/null +++ b/spring-acl/src/main/java/org/baeldung/acl/persistence/dao/NoticeMessageRepository.java @@ -0,0 +1,24 @@ +package org.baeldung.acl.persistence.dao; + +import java.util.List; + +import org.baeldung.acl.persistence.entity.NoticeMessage; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.repository.query.Param; +import org.springframework.security.access.prepost.PostAuthorize; +import org.springframework.security.access.prepost.PostFilter; +import org.springframework.security.access.prepost.PreAuthorize; + +public interface NoticeMessageRepository extends JpaRepository{ + + @PostFilter("hasPermission(filterObject, 'READ')") + List findAll(); + + @PostAuthorize("hasPermission(returnObject, 'READ')") + NoticeMessage findById(Integer id); + + @SuppressWarnings("unchecked") + @PreAuthorize("hasPermission(#noticeMessage, 'WRITE')") + NoticeMessage save(@Param("noticeMessage")NoticeMessage noticeMessage); + +} diff --git a/spring-acl/src/main/java/org/baeldung/acl/persistence/entity/NoticeMessage.java b/spring-acl/src/main/java/org/baeldung/acl/persistence/entity/NoticeMessage.java new file mode 100644 index 0000000000..23f01a8edb --- /dev/null +++ b/spring-acl/src/main/java/org/baeldung/acl/persistence/entity/NoticeMessage.java @@ -0,0 +1,29 @@ +package org.baeldung.acl.persistence.entity; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name="system_message") +public class NoticeMessage { + + @Id + @Column + private Integer id; + @Column + private String content; + public Integer getId() { + return id; + } + public void setId(Integer id) { + this.id = id; + } + public String getContent() { + return content; + } + public void setContent(String content) { + this.content = content; + } +} \ No newline at end of file diff --git a/spring-acl/src/main/resources/acl-data.sql b/spring-acl/src/main/resources/acl-data.sql new file mode 100644 index 0000000000..6c01eaacc2 --- /dev/null +++ b/spring-acl/src/main/resources/acl-data.sql @@ -0,0 +1,28 @@ +INSERT INTO system_message(id,content) VALUES (1,'First Level Message'); +INSERT INTO system_message(id,content) VALUES (2,'Second Level Message'); +INSERT INTO system_message(id,content) VALUES (3,'Third Level Message'); + +INSERT INTO acl_class (id, class) VALUES +(1, 'org.baeldung.acl.persistence.entity.NoticeMessage'); + +INSERT INTO acl_sid (id, principal, sid) VALUES +(1, 1, 'manager'), +(2, 1, 'hr'), +(3, 1, 'admin'), +(4, 0, 'ROLE_EDITOR'); + +INSERT INTO acl_object_identity (id, object_id_class, object_id_identity, parent_object, owner_sid, entries_inheriting) VALUES +(1, 1, 1, NULL, 3, 0), +(2, 1, 2, NULL, 3, 0), +(3, 1, 3, NULL, 3, 0) +; + +INSERT INTO acl_entry (id, acl_object_identity, ace_order, sid, mask, granting, audit_success, audit_failure) VALUES +(1, 1, 1, 1, 1, 1, 1, 1), +(2, 1, 2, 1, 2, 1, 1, 1), +(3, 1, 3, 4, 1, 1, 1, 1), +(4, 2, 1, 2, 1, 1, 1, 1), +(5, 2, 2, 4, 1, 1, 1, 1), +(6, 3, 1, 4, 1, 1, 1, 1), +(7, 3, 2, 4, 2, 1, 1, 1) +; \ No newline at end of file diff --git a/spring-acl/src/main/resources/acl-schema.sql b/spring-acl/src/main/resources/acl-schema.sql new file mode 100644 index 0000000000..58e9394b2b --- /dev/null +++ b/spring-acl/src/main/resources/acl-schema.sql @@ -0,0 +1,58 @@ +create table system_message (id integer not null, content varchar(255), primary key (id)); + +CREATE TABLE IF NOT EXISTS acl_sid ( + id bigint(20) NOT NULL AUTO_INCREMENT, + principal tinyint(1) NOT NULL, + sid varchar(100) NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY unique_uk_1 (sid,principal) +); + +CREATE TABLE IF NOT EXISTS acl_class ( + id bigint(20) NOT NULL AUTO_INCREMENT, + class varchar(255) NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY unique_uk_2 (class) +); + +CREATE TABLE IF NOT EXISTS acl_entry ( + id bigint(20) NOT NULL AUTO_INCREMENT, + acl_object_identity bigint(20) NOT NULL, + ace_order int(11) NOT NULL, + sid bigint(20) NOT NULL, + mask int(11) NOT NULL, + granting tinyint(1) NOT NULL, + audit_success tinyint(1) NOT NULL, + audit_failure tinyint(1) NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY unique_uk_4 (acl_object_identity,ace_order) +); + +CREATE TABLE IF NOT EXISTS acl_object_identity ( + id bigint(20) NOT NULL AUTO_INCREMENT, + object_id_class bigint(20) NOT NULL, + object_id_identity bigint(20) NOT NULL, + parent_object bigint(20) DEFAULT NULL, + owner_sid bigint(20) DEFAULT NULL, + entries_inheriting tinyint(1) NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY unique_uk_3 (object_id_class,object_id_identity) +); + +ALTER TABLE acl_entry +ADD FOREIGN KEY (acl_object_identity) REFERENCES acl_object_identity(id); + +ALTER TABLE acl_entry +ADD FOREIGN KEY (sid) REFERENCES acl_sid(id); + +-- +-- Constraints for table acl_object_identity +-- +ALTER TABLE acl_object_identity +ADD FOREIGN KEY (parent_object) REFERENCES acl_object_identity (id); + +ALTER TABLE acl_object_identity +ADD FOREIGN KEY (object_id_class) REFERENCES acl_class (id); + +ALTER TABLE acl_object_identity +ADD FOREIGN KEY (owner_sid) REFERENCES acl_sid (id); \ No newline at end of file diff --git a/spring-acl/src/main/resources/org.baeldung.acl.datasource.properties b/spring-acl/src/main/resources/org.baeldung.acl.datasource.properties new file mode 100644 index 0000000000..739dd3f07c --- /dev/null +++ b/spring-acl/src/main/resources/org.baeldung.acl.datasource.properties @@ -0,0 +1,12 @@ +spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE +spring.datasource.username=sa +spring.datasource.password= +spring.datasource.driverClassName=org.h2.Driver +spring.jpa.hibernate.ddl-auto=update +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect + +spring.h2.console.path=/myconsole +spring.h2.console.enabled=true +spring.datasource.initialize=true +spring.datasource.schema=classpath:acl-schema.sql +spring.datasource.data=classpath:acl-data.sql \ No newline at end of file diff --git a/spring-acl/src/test/java/org/baeldung/acl/SpringAclTest.java b/spring-acl/src/test/java/org/baeldung/acl/SpringAclTest.java new file mode 100644 index 0000000000..fd9069d9bc --- /dev/null +++ b/spring-acl/src/test/java/org/baeldung/acl/SpringAclTest.java @@ -0,0 +1,119 @@ +package org.baeldung.acl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.util.List; + +import org.baeldung.acl.persistence.dao.NoticeMessageRepository; +import org.baeldung.acl.persistence.entity.NoticeMessage; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; +import org.springframework.test.context.support.DirtiesContextTestExecutionListener; +import org.springframework.test.context.transaction.TransactionalTestExecutionListener; +import org.springframework.test.context.web.ServletTestExecutionListener; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +@TestExecutionListeners(listeners={ServletTestExecutionListener.class, + DependencyInjectionTestExecutionListener.class, + DirtiesContextTestExecutionListener.class, + TransactionalTestExecutionListener.class, + WithSecurityContextTestExecutionListener.class}) +public class SpringAclTest extends AbstractJUnit4SpringContextTests{ + + private static Integer FIRST_MESSAGE_ID = 1; + private static Integer SECOND_MESSAGE_ID = 2; + private static Integer THIRD_MESSAGE_ID = 3; + private static String EDITTED_CONTENT = "EDITED"; + + @Configuration + @ComponentScan("org.baeldung.acl.*") + public static class SpringConfig { + + } + + @Autowired + NoticeMessageRepository repo; + + @Test + @WithMockUser(username="manager") + public void givenUsernameManager_whenFindAllMessage_thenReturnFirstMessage(){ + List details = repo.findAll(); + assertNotNull(details); + assertEquals(1,details.size()); + assertEquals(FIRST_MESSAGE_ID,details.get(0).getId()); + } + + @Test + @WithMockUser(username="manager") + public void givenUsernameManager_whenFindFirstMessageByIdAndUpdateFirstMessageContent_thenOK(){ + NoticeMessage firstMessage = repo.findById(FIRST_MESSAGE_ID); + assertNotNull(firstMessage); + assertEquals(FIRST_MESSAGE_ID,firstMessage.getId()); + + firstMessage.setContent(EDITTED_CONTENT); + repo.save(firstMessage); + + NoticeMessage editedFirstMessage = repo.findById(FIRST_MESSAGE_ID); + assertNotNull(editedFirstMessage); + assertEquals(FIRST_MESSAGE_ID,editedFirstMessage.getId()); + assertEquals(EDITTED_CONTENT,editedFirstMessage.getContent()); + } + + @Test + @WithMockUser(username="hr") + public void givenUsernameHr_whenFindMessageById2_thenOK(){ + NoticeMessage secondMessage = repo.findById(SECOND_MESSAGE_ID); + assertNotNull(secondMessage); + assertEquals(SECOND_MESSAGE_ID,secondMessage.getId()); + } + + @Test(expected=AccessDeniedException.class) + @WithMockUser(username="hr") + public void givenUsernameHr_whenUpdateMessageWithId2_thenFail(){ + NoticeMessage secondMessage = new NoticeMessage(); + secondMessage.setId(SECOND_MESSAGE_ID); + secondMessage.setContent(EDITTED_CONTENT); + repo.save(secondMessage); + } + + @Test + @WithMockUser(roles={"EDITOR"}) + public void givenRoleEditor_whenFindAllMessage_thenReturnThreeMessage(){ + List details = repo.findAll(); + assertNotNull(details); + assertEquals(3,details.size()); + } + + @Test + @WithMockUser(roles={"EDITOR"}) + public void givenRoleEditor_whenUpdateThirdMessage_thenOK(){ + NoticeMessage thirdMessage = new NoticeMessage(); + thirdMessage.setId(THIRD_MESSAGE_ID); + thirdMessage.setContent(EDITTED_CONTENT); + repo.save(thirdMessage); + } + + @Test(expected=AccessDeniedException.class) + @WithMockUser(roles={"EDITOR"}) + public void givenRoleEditor_whenFindFirstMessageByIdAndUpdateFirstMessageContent_thenFail(){ + NoticeMessage firstMessage = repo.findById(FIRST_MESSAGE_ID); + assertNotNull(firstMessage); + assertEquals(FIRST_MESSAGE_ID,firstMessage.getId()); + firstMessage.setContent(EDITTED_CONTENT); + repo.save(firstMessage); + } +} + \ No newline at end of file From 37d13ac4f5d8fa44edd33862b258bd3df05d4fd0 Mon Sep 17 00:00:00 2001 From: Jose Carvajal Date: Fri, 1 Dec 2017 08:43:20 +0100 Subject: [PATCH 25/50] BAEL-1174: A Quick Guide to Spring Cloud Consul --- core-java/src/main/resources/countries.properties | 6 +++--- spring-cloud/pom.xml | 1 + spring-cloud/spring-cloud-stream/pom.xml | 1 - undertow/dependency-reduced-pom.xml | 2 +- xml/src/test/resources/example_dom4j_new.xml | 10 ---------- xml/src/test/resources/example_jaxb_new.xml | 9 --------- 6 files changed, 5 insertions(+), 24 deletions(-) delete mode 100644 xml/src/test/resources/example_dom4j_new.xml delete mode 100644 xml/src/test/resources/example_jaxb_new.xml diff --git a/core-java/src/main/resources/countries.properties b/core-java/src/main/resources/countries.properties index 3c1f53aded..e743b5a40b 100644 --- a/core-java/src/main/resources/countries.properties +++ b/core-java/src/main/resources/countries.properties @@ -1,3 +1,3 @@ -UK -US -Germany +UK +US +Germany diff --git a/spring-cloud/pom.xml b/spring-cloud/pom.xml index e6d78f292d..d11455f90c 100644 --- a/spring-cloud/pom.xml +++ b/spring-cloud/pom.xml @@ -19,6 +19,7 @@ spring-cloud-stream spring-cloud-connectors-heroku spring-cloud-aws + spring-cloud-consul pom diff --git a/spring-cloud/spring-cloud-stream/pom.xml b/spring-cloud/spring-cloud-stream/pom.xml index 1666a440b9..5ec24268d9 100644 --- a/spring-cloud/spring-cloud-stream/pom.xml +++ b/spring-cloud/spring-cloud-stream/pom.xml @@ -6,7 +6,6 @@ org.baeldung spring-cloud-stream - pom spring-cloud-stream diff --git a/undertow/dependency-reduced-pom.xml b/undertow/dependency-reduced-pom.xml index 6481c51285..e8fcd39fbb 100644 --- a/undertow/dependency-reduced-pom.xml +++ b/undertow/dependency-reduced-pom.xml @@ -36,4 +36,4 @@ 1.8 1.8 - \ No newline at end of file + diff --git a/xml/src/test/resources/example_dom4j_new.xml b/xml/src/test/resources/example_dom4j_new.xml deleted file mode 100644 index 020760fdd3..0000000000 --- a/xml/src/test/resources/example_dom4j_new.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - XML with Dom4J - XML handling with Dom4J - 14/06/2016 - Dom4J tech writer - - diff --git a/xml/src/test/resources/example_jaxb_new.xml b/xml/src/test/resources/example_jaxb_new.xml deleted file mode 100644 index 646d938869..0000000000 --- a/xml/src/test/resources/example_jaxb_new.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - Jaxb author - 04/02/2015 - XML Binding with Jaxb - XML with Jaxb - - From b6b3c476a749430b1bf4c6d7532b96909a50f732 Mon Sep 17 00:00:00 2001 From: Jose Carvajal Date: Fri, 1 Dec 2017 08:47:37 +0100 Subject: [PATCH 26/50] Update dependency-reduced-pom.xml From 978d5e630a68a40ca1e1cf92436fb77cc3604143 Mon Sep 17 00:00:00 2001 From: Jose Carvajal Date: Fri, 1 Dec 2017 08:48:36 +0100 Subject: [PATCH 27/50] BAEL-1174: A Quick Guide to Spring Cloud Consul --- ...ipleOutputsServiceApplicationUnitTest.java | 52 ------------------- ...eOutputsWithConditionsServiceUnitTest.java | 52 ------------------- .../rabbit/MyLoggerApplicationUnitTest.java | 40 -------------- undertow/dependency-reduced-pom.xml | 1 + 4 files changed, 1 insertion(+), 144 deletions(-) delete mode 100644 spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsServiceApplicationUnitTest.java delete mode 100644 spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsWithConditionsServiceUnitTest.java delete mode 100644 spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MyLoggerApplicationUnitTest.java diff --git a/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsServiceApplicationUnitTest.java b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsServiceApplicationUnitTest.java deleted file mode 100644 index 225ed73ff7..0000000000 --- a/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsServiceApplicationUnitTest.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.baeldung.spring.cloud.stream.rabbit; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cloud.stream.test.binder.MessageCollector; -import org.springframework.messaging.MessageChannel; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -import com.baeldung.spring.cloud.stream.rabbit.processor.MyProcessor; - -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = MultipleOutputsServiceApplication.class) -@DirtiesContext -public class MultipleOutputsServiceApplicationUnitTest { - - @Autowired - private MyProcessor pipe; - - @Autowired - private MessageCollector messageCollector; - - @Test - public void whenSendMessage_thenResponseIsInAOutput() { - whenSendMessage(1); - thenPayloadInChannelIs(pipe.anOutput(), 1); - } - - @Test - public void whenSendMessage_thenResponseIsInAnotherOutput() { - whenSendMessage(11); - thenPayloadInChannelIs(pipe.anotherOutput(), 11); - } - - private void whenSendMessage(Integer val) { - pipe.myInput() - .send(MessageBuilder.withPayload(val) - .build()); - } - - private void thenPayloadInChannelIs(MessageChannel channel, Integer expectedValue) { - Object payload = messageCollector.forChannel(channel) - .poll() - .getPayload(); - assertEquals(expectedValue, payload); - } -} diff --git a/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsWithConditionsServiceUnitTest.java b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsWithConditionsServiceUnitTest.java deleted file mode 100644 index 8628b209cb..0000000000 --- a/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MultipleOutputsWithConditionsServiceUnitTest.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.baeldung.spring.cloud.stream.rabbit; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cloud.stream.test.binder.MessageCollector; -import org.springframework.messaging.MessageChannel; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -import com.baeldung.spring.cloud.stream.rabbit.processor.MyProcessor; - -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = MultipleOutputsWithConditionsServiceApplication.class) -@DirtiesContext -public class MultipleOutputsWithConditionsServiceUnitTest { - - @Autowired - private MyProcessor pipe; - - @Autowired - private MessageCollector messageCollector; - - @Test - public void whenSendMessage_thenResponseIsInAOutput() { - whenSendMessage(1); - thenPayloadInChannelIs(pipe.anOutput(), 1); - } - - @Test - public void whenSendMessage_thenResponseIsInAnotherOutput() { - whenSendMessage(11); - thenPayloadInChannelIs(pipe.anotherOutput(), 11); - } - - private void whenSendMessage(Integer val) { - pipe.myInput() - .send(MessageBuilder.withPayload(val) - .build()); - } - - private void thenPayloadInChannelIs(MessageChannel channel, Integer expectedValue) { - Object payload = messageCollector.forChannel(channel) - .poll() - .getPayload(); - assertEquals(expectedValue, payload); - } -} diff --git a/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MyLoggerApplicationUnitTest.java b/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MyLoggerApplicationUnitTest.java deleted file mode 100644 index 1541af3cb5..0000000000 --- a/spring-cloud/spring-cloud-stream/spring-cloud-stream-rabbit/src/test/java/com/baeldung/spring/cloud/stream/rabbit/MyLoggerApplicationUnitTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.baeldung.spring.cloud.stream.rabbit; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cloud.stream.messaging.Processor; -import org.springframework.cloud.stream.test.binder.MessageCollector; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -import com.baeldung.spring.cloud.stream.rabbit.model.LogMessage; - -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = MyLoggerServiceApplication.class) -@DirtiesContext -public class MyLoggerApplicationUnitTest { - - @Autowired - private Processor pipe; - - @Autowired - private MessageCollector messageCollector; - - @Test - public void whenSendMessage_thenResponseShouldUpdateText() { - pipe.input() - .send(MessageBuilder.withPayload(new LogMessage("This is my message")) - .build()); - - Object payload = messageCollector.forChannel(pipe.output()) - .poll() - .getPayload(); - - assertEquals("[1]: This is my message", payload.toString()); - } -} diff --git a/undertow/dependency-reduced-pom.xml b/undertow/dependency-reduced-pom.xml index e8fcd39fbb..0654c82b74 100644 --- a/undertow/dependency-reduced-pom.xml +++ b/undertow/dependency-reduced-pom.xml @@ -37,3 +37,4 @@ 1.8 + From a94f98b63194deb3e204bd454b9eb829c18ab4bb Mon Sep 17 00:00:00 2001 From: Yasin Date: Fri, 1 Dec 2017 14:10:03 +0530 Subject: [PATCH 28/50] BAEL-1334 Guide to Hibernate Spatial (#3139) * BAEL-1334 Guide to Hibernate Spatial * BAEL-1334 Guide to Hibernate Spatial Moving the files to hibernate5 from libraries * Reverting the pom file --- hibernate5/pom.xml | 22 ++++- .../com/baeldung/hibernate/HibernateUtil.java | 14 ++- .../baeldung/hibernate/pojo/PointEntity.java | 41 ++++++++ .../hibernate/HibernateSpatialTest.java | 97 +++++++++++++++++++ .../resources/hibernate-spatial.properties | 10 ++ 5 files changed, 179 insertions(+), 5 deletions(-) create mode 100644 hibernate5/src/main/java/com/baeldung/hibernate/pojo/PointEntity.java create mode 100644 hibernate5/src/test/java/com/baeldung/hibernate/HibernateSpatialTest.java create mode 100644 hibernate5/src/test/resources/hibernate-spatial.properties diff --git a/hibernate5/pom.xml b/hibernate5/pom.xml index 8543d1dae3..3b0b2fcd88 100644 --- a/hibernate5/pom.xml +++ b/hibernate5/pom.xml @@ -17,12 +17,15 @@ UTF-8 3.6.0 - + 5.2.12.Final + 6.0.6 + 2.2.3 + org.hibernate hibernate-core - 5.2.12.Final + ${hibernate.version} junit @@ -40,6 +43,21 @@ h2 1.4.194 + + org.hibernate + hibernate-spatial + ${hibernate.version} + + + mysql + mysql-connector-java + ${mysql.version} + + + ch.vorburger.mariaDB4j + mariaDB4j + ${mariaDB4j.version} + hibernate5 diff --git a/hibernate5/src/main/java/com/baeldung/hibernate/HibernateUtil.java b/hibernate5/src/main/java/com/baeldung/hibernate/HibernateUtil.java index c3bf8c2aa9..f1fc22d29a 100644 --- a/hibernate5/src/main/java/com/baeldung/hibernate/HibernateUtil.java +++ b/hibernate5/src/main/java/com/baeldung/hibernate/HibernateUtil.java @@ -5,6 +5,7 @@ import com.baeldung.hibernate.pojo.EntityDescription; import com.baeldung.hibernate.pojo.OrderEntry; import com.baeldung.hibernate.pojo.OrderEntryIdClass; import com.baeldung.hibernate.pojo.OrderEntryPK; +import com.baeldung.hibernate.pojo.PointEntity; import com.baeldung.hibernate.pojo.Product; import com.baeldung.hibernate.pojo.Phone; import com.baeldung.hibernate.pojo.TemporalValues; @@ -23,6 +24,7 @@ import com.baeldung.hibernate.pojo.inheritance.Person; import com.baeldung.hibernate.pojo.inheritance.Pet; import com.baeldung.hibernate.pojo.inheritance.Vehicle; +import org.apache.commons.lang3.StringUtils; import org.hibernate.SessionFactory; import org.hibernate.boot.Metadata; import org.hibernate.boot.MetadataSources; @@ -36,8 +38,14 @@ import java.util.Properties; public class HibernateUtil { private static SessionFactory sessionFactory; + private static String PROPERTY_FILE_NAME; public static SessionFactory getSessionFactory() throws IOException { + return getSessionFactory(null); + } + + public static SessionFactory getSessionFactory(String propertyFileName) throws IOException { + PROPERTY_FILE_NAME = propertyFileName; if (sessionFactory == null) { ServiceRegistry serviceRegistry = configureServiceRegistry(); sessionFactory = makeSessionFactory(serviceRegistry); @@ -70,6 +78,7 @@ public class HibernateUtil { metadataSources.addAnnotatedClass(Vehicle.class); metadataSources.addAnnotatedClass(Car.class); metadataSources.addAnnotatedClass(Bag.class); + metadataSources.addAnnotatedClass(PointEntity.class); Metadata metadata = metadataSources.buildMetadata(); return metadata.getSessionFactoryBuilder() @@ -86,12 +95,11 @@ public class HibernateUtil { private static Properties getProperties() throws IOException { Properties properties = new Properties(); URL propertiesURL = Thread.currentThread() - .getContextClassLoader() - .getResource("hibernate.properties"); + .getContextClassLoader() + .getResource(StringUtils.defaultString(PROPERTY_FILE_NAME, "hibernate.properties")); try (FileInputStream inputStream = new FileInputStream(propertiesURL.getFile())) { properties.load(inputStream); } return properties; } - } \ No newline at end of file diff --git a/hibernate5/src/main/java/com/baeldung/hibernate/pojo/PointEntity.java b/hibernate5/src/main/java/com/baeldung/hibernate/pojo/PointEntity.java new file mode 100644 index 0000000000..223f5dcbde --- /dev/null +++ b/hibernate5/src/main/java/com/baeldung/hibernate/pojo/PointEntity.java @@ -0,0 +1,41 @@ +package com.baeldung.hibernate.pojo; + +import com.vividsolutions.jts.geom.Point; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +@Entity +public class PointEntity { + + @Id + @GeneratedValue + private Long id; + + private Point point; + + public PointEntity() { + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Point getPoint() { + return point; + } + + public void setPoint(Point point) { + this.point = point; + } + + @Override + public String toString() { + return "PointEntity{" + "id=" + id + ", point=" + point + '}'; + } +} diff --git a/hibernate5/src/test/java/com/baeldung/hibernate/HibernateSpatialTest.java b/hibernate5/src/test/java/com/baeldung/hibernate/HibernateSpatialTest.java new file mode 100644 index 0000000000..6d0aa0a4cd --- /dev/null +++ b/hibernate5/src/test/java/com/baeldung/hibernate/HibernateSpatialTest.java @@ -0,0 +1,97 @@ +package com.baeldung.hibernate; + +import com.baeldung.hibernate.pojo.PointEntity; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.Point; +import com.vividsolutions.jts.io.ParseException; +import com.vividsolutions.jts.io.WKTReader; +import org.hibernate.Session; +import org.hibernate.Transaction; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import javax.persistence.Query; +import java.io.IOException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class HibernateSpatialTest { + + private Session session; + private Transaction transaction; + + @Before + public void setUp() throws IOException { + session = HibernateUtil.getSessionFactory("hibernate-spatial.properties") + .openSession(); + transaction = session.beginTransaction(); + } + + @After + public void tearDown() { + transaction.rollback(); + session.close(); + } + + @Test + public void shouldConvertWktToGeometry() throws ParseException { + Geometry geometry = wktToGeometry("POINT (2 5)"); + assertEquals("Point", geometry.getGeometryType()); + assertTrue(geometry instanceof Point); + } + + @Test + public void shouldInsertAndSelectPoints() throws ParseException { + PointEntity entity = new PointEntity(); + entity.setPoint((Point) wktToGeometry("POINT (1 1)")); + + session.persist(entity); + PointEntity fromDb = session.find(PointEntity.class, entity.getId()); + assertEquals("POINT (1 1)", fromDb.getPoint().toString()); + } + + @Test + public void shouldSelectDisjointPoints() throws ParseException { + insertPoint("POINT (1 2)"); + insertPoint("POINT (3 4)"); + insertPoint("POINT (5 6)"); + + Point point = (Point) wktToGeometry("POINT (3 4)"); + Query query = session.createQuery("select p from PointEntity p " + + "where disjoint(p.point, :point) = true", PointEntity.class); + query.setParameter("point", point); + assertEquals("POINT (1 2)", ((PointEntity) query.getResultList().get(0)).getPoint().toString()); + assertEquals("POINT (5 6)", ((PointEntity) query.getResultList().get(1)).getPoint().toString()); + } + + @Test + public void shouldSelectAllPointsWithinPolygon() throws ParseException { + insertPoint("POINT (1 1)"); + insertPoint("POINT (1 2)"); + insertPoint("POINT (3 4)"); + insertPoint("POINT (5 6)"); + + Query query = session.createQuery("select p from PointEntity p where within(p.point, :area) = true", + PointEntity.class); + query.setParameter("area", wktToGeometry("POLYGON((0 0, 0 5, 5 5, 5 0, 0 0))")); + assertEquals(3, query.getResultList().size()); + assertEquals("POINT (1 1)", ((PointEntity) query.getResultList().get(0)).getPoint().toString()); + assertEquals("POINT (1 2)", ((PointEntity) query.getResultList().get(1)).getPoint().toString()); + assertEquals("POINT (3 4)", ((PointEntity) query.getResultList().get(2)).getPoint().toString()); + } + + private void insertPoint(String point) throws ParseException { + PointEntity entity = new PointEntity(); + entity.setPoint((Point) wktToGeometry(point)); + session.persist(entity); + } + + private Geometry wktToGeometry(String wellKnownText) throws ParseException { + WKTReader fromText = new WKTReader(); + Geometry geom = null; + geom = fromText.read(wellKnownText); + return geom; + } +} diff --git a/hibernate5/src/test/resources/hibernate-spatial.properties b/hibernate5/src/test/resources/hibernate-spatial.properties new file mode 100644 index 0000000000..e85cd49cc3 --- /dev/null +++ b/hibernate5/src/test/resources/hibernate-spatial.properties @@ -0,0 +1,10 @@ +hibernate.dialect=org.hibernate.spatial.dialect.mysql.MySQL56SpatialDialect +hibernate.connection.driver_class=com.mysql.jdbc.Driver +hibernate.connection.url=jdbc:mysql://localhost:3306/hibernate-spatial +hibernate.connection.username=root +hibernate.connection.password=pass +hibernate.connection.pool_size=5 +hibernate.show_sql=true +hibernate.format_sql=true +hibernate.max_fetch_depth=5 +hibernate.hbm2ddl.auto=create-drop \ No newline at end of file From 5aa0b890d1fcb90bb3d0bf43bb1689ff27dee252 Mon Sep 17 00:00:00 2001 From: Loredana Crusoveanu Date: Fri, 1 Dec 2017 13:34:00 +0200 Subject: [PATCH 29/50] refactor packages --- .../DiscoveryClientApplication.java | 30 +++++++++---------- .../ServiceDiscoveryApplication.java | 2 +- .../DistributedPropertiesApplication.java | 4 +-- .../{ => ribbon}/RibbonClientApplication.java | 14 ++++++--- 4 files changed, 26 insertions(+), 24 deletions(-) rename spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/{ => discovery}/DiscoveryClientApplication.java (64%) rename spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/{ => health}/ServiceDiscoveryApplication.java (94%) rename spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/{ => properties}/DistributedPropertiesApplication.java (90%) rename spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/{ => ribbon}/RibbonClientApplication.java (74%) diff --git a/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/DiscoveryClientApplication.java b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/discovery/DiscoveryClientApplication.java similarity index 64% rename from spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/DiscoveryClientApplication.java rename to spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/discovery/DiscoveryClientApplication.java index e5e18d71ff..2dfd08b5c3 100644 --- a/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/DiscoveryClientApplication.java +++ b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/discovery/DiscoveryClientApplication.java @@ -1,16 +1,14 @@ -package com.baeldung.spring.cloud.consul; +package com.baeldung.spring.cloud.consul.discovery; import java.net.URI; -import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.context.annotation.Bean; -import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @@ -24,26 +22,26 @@ public class DiscoveryClientApplication { return new RestTemplate(); } - @Autowired - private RestTemplate restTemplate; - @Autowired private DiscoveryClient discoveryClient; - @RequestMapping("/discoveryClient") + @GetMapping("/discoveryClient") public String home() { - return this.restTemplate.getForEntity(serviceUrl().resolve("/ping"), String.class) + return restTemplate().getForEntity(serviceUrl().resolve("/ping"), String.class) .getBody(); } + + @GetMapping("/ping") + public String ping() { + return "pong"; + } public URI serviceUrl() { - List list = discoveryClient.getInstances("myApp"); - if (list != null && list.size() > 0) { - return list.get(0) - .getUri(); - } - - return null; + return discoveryClient.getInstances("myApp") + .stream() + .findFirst() + .map(si -> si.getUri()) + .orElse(null); } public static void main(String[] args) { diff --git a/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/ServiceDiscoveryApplication.java b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/health/ServiceDiscoveryApplication.java similarity index 94% rename from spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/ServiceDiscoveryApplication.java rename to spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/health/ServiceDiscoveryApplication.java index 5da8105d01..7db361eb4f 100644 --- a/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/ServiceDiscoveryApplication.java +++ b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/health/ServiceDiscoveryApplication.java @@ -1,4 +1,4 @@ -package com.baeldung.spring.cloud.consul; +package com.baeldung.spring.cloud.consul.health; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; diff --git a/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/DistributedPropertiesApplication.java b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/properties/DistributedPropertiesApplication.java similarity index 90% rename from spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/DistributedPropertiesApplication.java rename to spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/properties/DistributedPropertiesApplication.java index 12ecd96b26..919bf08921 100644 --- a/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/DistributedPropertiesApplication.java +++ b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/properties/DistributedPropertiesApplication.java @@ -1,4 +1,4 @@ -package com.baeldung.spring.cloud.consul; +package com.baeldung.spring.cloud.consul.properties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -7,8 +7,6 @@ import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import com.baeldung.spring.cloud.consul.properties.MyProperties; - @SpringBootApplication @RestController public class DistributedPropertiesApplication { diff --git a/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/RibbonClientApplication.java b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/ribbon/RibbonClientApplication.java similarity index 74% rename from spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/RibbonClientApplication.java rename to spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/ribbon/RibbonClientApplication.java index 620c3553ba..ca0d23b185 100644 --- a/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/RibbonClientApplication.java +++ b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/ribbon/RibbonClientApplication.java @@ -1,10 +1,11 @@ -package com.baeldung.spring.cloud.consul; +package com.baeldung.spring.cloud.consul.ribbon; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @@ -15,16 +16,21 @@ public class RibbonClientApplication { @LoadBalanced @Bean - public RestTemplate loadbalancedRestTemplate() { + RestTemplate getRestTemplate() { return new RestTemplate(); } - + @Autowired RestTemplate restTemplate; @RequestMapping("/ribbonClient") public String home() { - return this.restTemplate.getForObject("http://myApp/ping", String.class); + return restTemplate.getForObject("http://myApp/ping", String.class); + } + + @GetMapping("/ping") + public String ping() { + return "pong"; } public static void main(String[] args) { From cd14a791d120c1c7cebc1f575ce4027bd45ed37d Mon Sep 17 00:00:00 2001 From: Loredana Crusoveanu Date: Fri, 1 Dec 2017 13:56:44 +0200 Subject: [PATCH 30/50] remove extra word file --- libraries/helloWorld.docx | Bin 76887 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 libraries/helloWorld.docx diff --git a/libraries/helloWorld.docx b/libraries/helloWorld.docx deleted file mode 100644 index a58ff2d80abbfc2881bc7cd799f996a58e58bd0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76887 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 AsQ>@~ From 69fc1a02f4482639e991d20bb277b626b4c0e150 Mon Sep 17 00:00:00 2001 From: Loredana Crusoveanu Date: Fri, 1 Dec 2017 14:36:59 +0200 Subject: [PATCH 31/50] fix cas project build --- cas/cas-secured-app/pom.xml | 5 +++++ .../baeldung/cassecuredapp/controllers/AuthController.java | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/cas/cas-secured-app/pom.xml b/cas/cas-secured-app/pom.xml index 543354e8b3..13fb7ac2cb 100644 --- a/cas/cas-secured-app/pom.xml +++ b/cas/cas-secured-app/pom.xml @@ -57,6 +57,11 @@ spring-security-test test + + org.apache.logging.log4j + log4j-core + 2.10.0 + diff --git a/cas/cas-secured-app/src/main/java/com/baeldung/cassecuredapp/controllers/AuthController.java b/cas/cas-secured-app/src/main/java/com/baeldung/cassecuredapp/controllers/AuthController.java index 703e6abf7a..7faccbb125 100644 --- a/cas/cas-secured-app/src/main/java/com/baeldung/cassecuredapp/controllers/AuthController.java +++ b/cas/cas-secured-app/src/main/java/com/baeldung/cassecuredapp/controllers/AuthController.java @@ -1,6 +1,7 @@ package com.baeldung.cassecuredapp.controllers; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.logout.CookieClearingLogoutHandler; @@ -15,7 +16,7 @@ import javax.servlet.http.HttpServletResponse; @Controller public class AuthController { - private Logger logger = Logger.getLogger(AuthController.class); + private Logger logger = LogManager.getLogger(AuthController.class); @GetMapping("/logout") public String logout( From 4e61c381e3fdcc67fb9f59efd50d7b359f4ce72b Mon Sep 17 00:00:00 2001 From: Loredana Crusoveanu Date: Fri, 1 Dec 2017 14:43:27 +0200 Subject: [PATCH 32/50] remove extra depedency --- cas/cas-secured-app/pom.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/cas/cas-secured-app/pom.xml b/cas/cas-secured-app/pom.xml index 13fb7ac2cb..543354e8b3 100644 --- a/cas/cas-secured-app/pom.xml +++ b/cas/cas-secured-app/pom.xml @@ -57,11 +57,6 @@ spring-security-test test - - org.apache.logging.log4j - log4j-core - 2.10.0 - From f00c95f4fc2973ddc0e3cc7b62a259307bc0156d Mon Sep 17 00:00:00 2001 From: Graham Cox Date: Fri, 1 Dec 2017 14:05:24 +0000 Subject: [PATCH 33/50] Examples of writing Extension Methods --- .../com/baeldung/kotlin/ExtensionMethods.kt | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 core-kotlin/src/test/kotlin/com/baeldung/kotlin/ExtensionMethods.kt diff --git a/core-kotlin/src/test/kotlin/com/baeldung/kotlin/ExtensionMethods.kt b/core-kotlin/src/test/kotlin/com/baeldung/kotlin/ExtensionMethods.kt new file mode 100644 index 0000000000..09ce898860 --- /dev/null +++ b/core-kotlin/src/test/kotlin/com/baeldung/kotlin/ExtensionMethods.kt @@ -0,0 +1,51 @@ +package com.baeldung.kotlin + +import org.junit.Assert +import org.junit.Test + +class ExtensionMethods { + @Test + fun simpleExtensionMethod() { + fun String.escapeForXml() : String { + return this + .replace("&", "&") + .replace("<", "<") + .replace(">", ">") + } + + Assert.assertEquals("Nothing", "Nothing".escapeForXml()) + Assert.assertEquals("<Tag>", "".escapeForXml()) + Assert.assertEquals("a&b", "a&b".escapeForXml()) + } + + @Test + fun genericExtensionMethod() { + fun T.concatAsString(b: T) : String { + return this.toString() + b.toString() + } + + Assert.assertEquals("12", "1".concatAsString("2")) + Assert.assertEquals("12", 1.concatAsString(2)) + // This doesn't compile + // Assert.assertEquals("12", 1.concatAsString(2.0)) + } + + @Test + fun infixExtensionMethod() { + infix fun Number.toPowerOf(exponent: Number): Double { + return Math.pow(this.toDouble(), exponent.toDouble()) + } + + Assert.assertEquals(9.0, 3 toPowerOf 2, 0.1) + Assert.assertEquals(3.0, 9 toPowerOf 0.5, 0.1) + } + + @Test + fun operatorExtensionMethod() { + operator fun List.times(by: Int): List { + return this.map { it * by } + } + + Assert.assertEquals(listOf(2, 4, 6), listOf(1, 2, 3) * 2) + } +} From bdf881701c8676714301b5865fdc1b3bddc8b32a Mon Sep 17 00:00:00 2001 From: Loredana Crusoveanu Date: Fri, 1 Dec 2017 16:11:07 +0200 Subject: [PATCH 34/50] add headers ex --- core-java-9/run-httpclient.bat | 1 + .../com/baeldung/httpclient/HttpClientExample.java | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 core-java-9/run-httpclient.bat diff --git a/core-java-9/run-httpclient.bat b/core-java-9/run-httpclient.bat new file mode 100644 index 0000000000..60b1eb7f68 --- /dev/null +++ b/core-java-9/run-httpclient.bat @@ -0,0 +1 @@ +java --module-path mods -m com.baeldung.httpclient/com.baeldung.httpclient.HttpClientExample \ No newline at end of file diff --git a/core-java-9/src/modules/com.baeldung.httpclient/com/baeldung/httpclient/HttpClientExample.java b/core-java-9/src/modules/com.baeldung.httpclient/com/baeldung/httpclient/HttpClientExample.java index 44c55715c7..de08c2164e 100644 --- a/core-java-9/src/modules/com.baeldung.httpclient/com/baeldung/httpclient/HttpClientExample.java +++ b/core-java-9/src/modules/com.baeldung.httpclient/com/baeldung/httpclient/HttpClientExample.java @@ -28,7 +28,7 @@ public class HttpClientExample { public static void main(String[] args) throws Exception { httpGetRequest(); - httpPosttRequest(); + httpPostRequest(); asynchronousRequest(); asynchronousMultipleRequests(); } @@ -36,14 +36,15 @@ public class HttpClientExample { public static void httpGetRequest() throws URISyntaxException, IOException, InterruptedException { HttpClient client = HttpClient.newHttpClient(); URI httpURI = new URI("http://jsonplaceholder.typicode.com/posts/1"); - HttpRequest request = HttpRequest.newBuilder(httpURI).GET().build(); + HttpRequest request = HttpRequest.newBuilder(httpURI).GET() + .headers("Accept-Enconding", "gzip, deflate").build(); HttpResponse response = client.send(request, HttpResponse.BodyHandler.asString()); String responseBody = response.body(); int responseStatusCode = response.statusCode(); System.out.println(responseBody); } - public static void httpPosttRequest() throws URISyntaxException, IOException, InterruptedException { + public static void httpPostRequest() throws URISyntaxException, IOException, InterruptedException { HttpClient client = HttpClient .newBuilder() .build(); From 1f7572884b8fac46762ff4d0c7977510d230ce15 Mon Sep 17 00:00:00 2001 From: Jose Carvajal Date: Fri, 1 Dec 2017 16:56:34 +0100 Subject: [PATCH 35/50] BAEL-1174: A Quick Guide to Spring Cloud Consul: Fix Ribbon client sasd a --- .../consul/discovery/DiscoveryClientApplication.java | 10 +++++++++- .../cloud/consul/ribbon/RibbonClientApplication.java | 11 +++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/discovery/DiscoveryClientApplication.java b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/discovery/DiscoveryClientApplication.java index 2dfd08b5c3..b4f42219a4 100644 --- a/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/discovery/DiscoveryClientApplication.java +++ b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/discovery/DiscoveryClientApplication.java @@ -8,7 +8,10 @@ import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.context.annotation.Bean; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @@ -30,12 +33,17 @@ public class DiscoveryClientApplication { return restTemplate().getForEntity(serviceUrl().resolve("/ping"), String.class) .getBody(); } - + @GetMapping("/ping") public String ping() { return "pong"; } + @RequestMapping("/my-health-check") + public ResponseEntity myCustomCheck() { + return new ResponseEntity<>(HttpStatus.OK); + } + public URI serviceUrl() { return discoveryClient.getInstances("myApp") .stream() diff --git a/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/ribbon/RibbonClientApplication.java b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/ribbon/RibbonClientApplication.java index ca0d23b185..b2b54fbe8e 100644 --- a/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/ribbon/RibbonClientApplication.java +++ b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/ribbon/RibbonClientApplication.java @@ -5,6 +5,8 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -19,7 +21,7 @@ public class RibbonClientApplication { RestTemplate getRestTemplate() { return new RestTemplate(); } - + @Autowired RestTemplate restTemplate; @@ -27,12 +29,17 @@ public class RibbonClientApplication { public String home() { return restTemplate.getForObject("http://myApp/ping", String.class); } - + @GetMapping("/ping") public String ping() { return "pong"; } + @RequestMapping("/my-health-check") + public ResponseEntity myCustomCheck() { + return new ResponseEntity<>(HttpStatus.OK); + } + public static void main(String[] args) { new SpringApplicationBuilder(RibbonClientApplication.class).web(true) .run(args); From 39fb51e63d1471dde1d89b0c0ecb045545b17a6f Mon Sep 17 00:00:00 2001 From: daoire Date: Fri, 1 Dec 2017 16:06:12 +0000 Subject: [PATCH 36/50] Update README.md --- spring-5/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-5/README.md b/spring-5/README.md index 15e12cb5dc..1b65d01811 100644 --- a/spring-5/README.md +++ b/spring-5/README.md @@ -10,4 +10,4 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring - [Exploring the Spring 5 MVC URL Matching Improvements](http://www.baeldung.com/spring-5-mvc-url-matching) - [Spring 5 WebClient](http://www.baeldung.com/spring-5-webclient) - [Spring 5 Functional Bean Registration](http://www.baeldung.com/spring-5-functional-beans) - +- [The SpringJUnitConfig and SpringJUnitWebConfig Annotations in Spring 5](http://www.baeldung.com/spring-5-junit-config) From b7a86b1b5644c4fa648d6a99a06d6b7cadc5ce31 Mon Sep 17 00:00:00 2001 From: Michael C Good <31810784+michaelcgood@users.noreply.github.com> Date: Fri, 1 Dec 2017 13:28:09 -0500 Subject: [PATCH 37/50] BAEL-1341 JDBC RowSet Interface (#3175) * michael.good703@gmail.com michael.good703@gmail.com * michael.good703@gmail.com michael.good703@gmail.com * michael.good703@gmail.com michael.good703@gmail.com * update * michael.good703@gmail.com Had to add @SpringBootApplication(exclude = MySQLAutoconfiguration.class) * Updated for 3.3.0.Final BAEL-1238 * Update pom.xml * BAEL-1238 Added new module spring-boot-keycloak and removed Keycloak code from spring-boot module * Minor changes to pom.xml * Update CustomConverterTest.java * Update StringToEmployeeConverter.java * Update GenericBigDecimalConverter.java * Update MyFeatures.java * Update .gitignore * Formatting changes * "Resolving conflicts" * Updated spring-boot to remove keycloak * Updated to see * Update * Updated * Found remnant file and deleted it * Update pom.xml Added spring-boot-keycloak module * Added reference to parent-boot-5 I changed the parent from org.springframework.boot to parent-boot-5. * Update GenericBigDecimalConverter.java Copy current GenericBigDecimalConverter to resolve any conflicts * Update StringToEmployeeConverter.java Copy current version to resolve any conflicts * Update pom.xml * Update pom.xml * Delete remnant files * Updated pom.xml * Update pom.xml * BAEL-1341 JDBC RowSet Interface This commit includes the main package and test package to accompany the tutorial JDBC RowSet Interface. * Update pom.xml * Changed from MySQL to h2 Changed MySQL to embedded database for ease of use. --- core-java/customers.xml | 95 ++ core-java/pom.xml | 921 +++++++++--------- .../jdbcrowset/DatabaseConfiguration.java | 51 + .../baeldung/jdbcrowset/ExampleListener.java | 24 + .../baeldung/jdbcrowset/FilterExample.java | 46 + .../jdbcrowset/JdbcRowsetApplication.java | 139 +++ .../baeldung/jdbcrowset/JdbcRowSetTest.java | 157 +++ 7 files changed, 981 insertions(+), 452 deletions(-) create mode 100644 core-java/customers.xml create mode 100644 core-java/src/main/java/com/baeldung/jdbcrowset/DatabaseConfiguration.java create mode 100644 core-java/src/main/java/com/baeldung/jdbcrowset/ExampleListener.java create mode 100644 core-java/src/main/java/com/baeldung/jdbcrowset/FilterExample.java create mode 100644 core-java/src/main/java/com/baeldung/jdbcrowset/JdbcRowsetApplication.java create mode 100644 core-java/src/test/java/com/baeldung/jdbcrowset/JdbcRowSetTest.java diff --git a/core-java/customers.xml b/core-java/customers.xml new file mode 100644 index 0000000000..b52dc27633 --- /dev/null +++ b/core-java/customers.xml @@ -0,0 +1,95 @@ + + + + SELECT * FROM customers + 1008 + + true + 1000 + 0 + 2 + + + + + 0 + 0 + 0 + true + ResultSet.TYPE_SCROLL_INSENSITIVE + false + customers + jdbc:h2:mem:testdb + + com.sun.rowset.providers.RIOptimisticProvider + Oracle Corporation + 1.0 + 2 + 1 + + + + 2 + + 1 + false + true + false + 0 + true + true + 11 + ID + ID + PUBLIC + 10 + 0 + CUSTOMERS + TESTDB + 4 + INTEGER + + + 2 + false + true + false + 0 + true + true + 50 + NAME + NAME + PUBLIC + 50 + 0 + CUSTOMERS + TESTDB + 12 + VARCHAR + + + + + 1 + Customer1 + + + 2 + Customer2 + + + 3 + Customer3 + + + 4 + Customer4 + + + 5 + Customer5 + + + diff --git a/core-java/pom.xml b/core-java/pom.xml index 2c4cbfc37b..068a772c43 100644 --- a/core-java/pom.xml +++ b/core-java/pom.xml @@ -1,479 +1,496 @@ - 4.0.0 - com.baeldung - core-java - 0.1.0-SNAPSHOT - jar + 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 + core-java + 0.1.0-SNAPSHOT + jar - core-java + core-java - + - - - net.sourceforge.collections - collections-generic - ${collections-generic.version} - - - com.google.guava - guava - ${guava.version} - - - - org.apache.commons - commons-collections4 - ${commons-collections4.version} - - - - commons-io - commons-io - ${commons-io.version} - - - - org.apache.commons - commons-lang3 - ${commons-lang3.version} - - - - org.apache.commons - commons-math3 - ${commons-math3.version} - - - - org.decimal4j - decimal4j - ${decimal4j.version} - - - - org.bouncycastle - bcprov-jdk15on - ${bouncycastle.version} - - - - org.unix4j - unix4j-command - ${unix4j.version} - - - - com.googlecode.grep4j - grep4j - ${grep4j.version} - - - - - - - com.fasterxml.jackson.core - jackson-databind - ${jackson.version} - - - - - log4j - log4j - 1.2.17 - - - org.slf4j - slf4j-api - ${org.slf4j.version} - - - ch.qos.logback - logback-classic - ${logback.version} - - - - org.slf4j - jcl-over-slf4j - ${org.slf4j.version} - - - - org.slf4j - log4j-over-slf4j - ${org.slf4j.version} - - - org.projectlombok - lombok - ${lombok.version} - provided - - - - - - org.hamcrest - hamcrest-all - 1.3 - test - - - - junit - junit - ${junit.version} - test - - - - org.hamcrest - hamcrest-core - ${org.hamcrest.version} - test - - - org.hamcrest - hamcrest-library - ${org.hamcrest.version} - test - - - - org.assertj - assertj-core - ${assertj.version} - test - - - - org.mockito - mockito-core - ${mockito.version} - test - - - com.jayway.awaitility - awaitility - ${avaitility.version} - test - - - - commons-codec - commons-codec - ${commons-codec.version} - - - - org.javamoney - moneta - 1.1 - - - - org.owasp.esapi - esapi - 2.1.0.1 - - - - com.codepoetics - protonpack - ${protonpack.version} - - - one.util - streamex - ${streamex.version} - - - io.vavr - vavr - ${vavr.version} - - - org.openjdk.jmh - jmh-core - 1.19 - - - org.openjdk.jmh - jmh-generator-annprocess - 1.19 - + - org.springframework - spring-web - 4.3.4.RELEASE + net.sourceforge.collections + collections-generic + ${collections-generic.version} + + + com.google.guava + guava + ${guava.version} - - - core-java - - - src/main/resources - true - - + + org.apache.commons + commons-collections4 + ${commons-collections4.version} + - + + commons-io + commons-io + ${commons-io.version} + - - org.apache.maven.plugins - maven-compiler-plugin - ${maven-compiler-plugin.version} - - 1.8 - 1.8 - - + + org.apache.commons + commons-lang3 + ${commons-lang3.version} + - - org.apache.maven.plugins - maven-surefire-plugin - - - **/*LiveTest.java - **/*IntegrationTest.java - **/*LongRunningUnitTest.java - **/*ManualTest.java - + + org.apache.commons + commons-math3 + ${commons-math3.version} + - - + + org.decimal4j + decimal4j + ${decimal4j.version} + - - org.apache.maven.plugins - maven-dependency-plugin - - - copy-dependencies - prepare-package - - copy-dependencies - - - ${project.build.directory}/libs - - - - + + org.bouncycastle + bcprov-jdk15on + ${bouncycastle.version} + - - org.apache.maven.plugins - maven-jar-plugin - - - - true - libs/ - org.baeldung.executable.ExecutableMavenJar - - - - + + org.unix4j + unix4j-command + ${unix4j.version} + - - org.apache.maven.plugins - maven-assembly-plugin - - - package - - single - - - ${project.basedir} - - - org.baeldung.executable.ExecutableMavenJar - - - - jar-with-dependencies - - - - - + + com.googlecode.grep4j + grep4j + ${grep4j.version} + + - - org.apache.maven.plugins - maven-shade-plugin - - - - shade - - - true - - - org.baeldung.executable.ExecutableMavenJar - - - - - - + - - com.jolira - onejar-maven-plugin - - - - org.baeldung.executable.ExecutableMavenJar - true - ${project.build.finalName}-onejar.${project.packaging} - - - one-jar - - - - + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + - - org.springframework.boot - spring-boot-maven-plugin - - - - repackage - - - spring-boot - org.baeldung.executable.ExecutableMavenJar - - - - + + + log4j + log4j + 1.2.17 + + + org.slf4j + slf4j-api + ${org.slf4j.version} + + + ch.qos.logback + logback-classic + ${logback.version} + + + + org.slf4j + jcl-over-slf4j + ${org.slf4j.version} + + + + org.slf4j + log4j-over-slf4j + ${org.slf4j.version} + + + org.projectlombok + lombok + ${lombok.version} + provided + - - org.codehaus.mojo - exec-maven-plugin - 1.6.0 - - java - com.baeldung.outofmemoryerror.OutOfMemoryGCLimitExceed - - -Xmx300m - -XX:+UseParallelGC - -classpath - - com.baeldung.outofmemoryerror.OutOfMemoryGCLimitExceed - - - + + + + org.hamcrest + hamcrest-all + 1.3 + test + + + + junit + junit + ${junit.version} + test + + + + org.hamcrest + hamcrest-core + ${org.hamcrest.version} + test + + + org.hamcrest + hamcrest-library + ${org.hamcrest.version} + test + + + + org.assertj + assertj-core + ${assertj.version} + test + + + + org.mockito + mockito-core + ${mockito.version} + test + + + com.jayway.awaitility + awaitility + ${avaitility.version} + test + + + + commons-codec + commons-codec + ${commons-codec.version} + + + + org.javamoney + moneta + 1.1 + + + + org.owasp.esapi + esapi + 2.1.0.1 + + + com.h2database + h2 + 1.4.196 + runtime + + + com.sun.messaging.mq + fscontext + ${fscontext.version} + + + com.codepoetics + protonpack + ${protonpack.version} + + + one.util + streamex + ${streamex.version} + + + io.vavr + vavr + ${vavr.version} + + + org.openjdk.jmh + jmh-core + 1.19 + + + org.openjdk.jmh + jmh-generator-annprocess + 1.19 + + + org.springframework + spring-web + 4.3.4.RELEASE + + + + org.springframework.boot + spring-boot-starter + 1.5.8.RELEASE + + + + + + core-java + + + src/main/resources + true + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + 1.8 + 1.8 + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*LiveTest.java + **/*IntegrationTest.java + **/*LongRunningUnitTest.java + **/*ManualTest.java + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-dependencies + prepare-package + + copy-dependencies + + + ${project.build.directory}/libs + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + true + libs/ + org.baeldung.executable.ExecutableMavenJar + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + package + + single + + + ${project.basedir} + + + org.baeldung.executable.ExecutableMavenJar + + + + jar-with-dependencies + + + + + + + + org.apache.maven.plugins + maven-shade-plugin + + + + shade + + + true + + + org.baeldung.executable.ExecutableMavenJar + + + + + + + + + com.jolira + onejar-maven-plugin + + + + org.baeldung.executable.ExecutableMavenJar + true + ${project.build.finalName}-onejar.${project.packaging} + + + one-jar + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + spring-boot + org.baeldung.executable.ExecutableMavenJar + + + + + + + org.codehaus.mojo + exec-maven-plugin + 1.6.0 + + java + com.baeldung.outofmemoryerror.OutOfMemoryGCLimitExceed + + -Xmx300m + -XX:+UseParallelGC + -classpath + + com.baeldung.outofmemoryerror.OutOfMemoryGCLimitExceed + + + - + - + - - - integration - - - - org.apache.maven.plugins - maven-surefire-plugin - - - integration-test - - test - - - - **/*ManualTest.java - - - **/*IntegrationTest.java - - - - - - - json - - - - - org.codehaus.mojo - exec-maven-plugin + + + integration + + + + org.apache.maven.plugins + maven-surefire-plugin + + + integration-test + + test + + + + **/*ManualTest.java + + + **/*IntegrationTest.java + + + + + + + json + + + + + org.codehaus.mojo + exec-maven-plugin - - - run-benchmarks - - none - - exec - - - test - java - - -classpath - - org.openjdk.jmh.Main - .* - - - - - - - - - + + + run-benchmarks + + none + + exec + + + test + java + + -classpath + + org.openjdk.jmh.Main + .* + + + + + + + + + - - - 2.8.5 + + + 2.8.5 - - 1.7.21 - 1.1.7 + + 1.7.21 + 1.1.7 - - 23.0 - 3.5 - 1.55 - 1.10 - 3.6.1 - 1.0.3 - 2.5 - 4.1 - 4.01 - 0.4 - 1.8.7 - 1.16.12 - 4.6-b01 - 1.13 - 0.6.5 - 0.9.0 - - - 1.3 - 4.12 - 2.8.9 - 3.6.1 - 1.7.0 + + 22.0 + 3.5 + 1.55 + 1.10 + 3.6.1 + 1.0.3 + 2.5 + 4.1 + 4.01 + 0.4 + 1.8.7 + 1.16.12 + 4.6-b01 + 1.13 + 0.6.5 + 0.9.0 - - 3.6.0 - 2.19.1 - - \ No newline at end of file + + 1.3 + 4.12 + 2.8.9 + 3.6.1 + 1.7.0 + + + 3.6.0 + 2.19.1 + + diff --git a/core-java/src/main/java/com/baeldung/jdbcrowset/DatabaseConfiguration.java b/core-java/src/main/java/com/baeldung/jdbcrowset/DatabaseConfiguration.java new file mode 100644 index 0000000000..9cfcff468e --- /dev/null +++ b/core-java/src/main/java/com/baeldung/jdbcrowset/DatabaseConfiguration.java @@ -0,0 +1,51 @@ +package com.baeldung.jdbcrowset; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; + +import javax.sql.rowset.JdbcRowSet; +import javax.sql.rowset.RowSetFactory; +import javax.sql.rowset.RowSetProvider; + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableAutoConfiguration +public class DatabaseConfiguration { + + + public static Connection geth2Connection() throws Exception { + Class.forName("org.h2.Driver"); + System.out.println("Driver Loaded."); + String url = "jdbc:h2:mem:testdb"; + return DriverManager.getConnection(url, "sa", ""); + } + + public static void initDatabase(Statement stmt) throws SQLException{ + int iter = 1; + while(iter<=5){ + String customer = "Customer"+iter; + String sql ="INSERT INTO customers(id, name) VALUES ("+iter+ ",'"+customer+"');"; + System.out.println("here is sql statmeent for execution: " + sql); + stmt.executeUpdate(sql); + iter++; + } + + int iterb = 1; + while(iterb<=5){ + String associate = "Associate"+iter; + String sql = "INSERT INTO associates(id, name) VALUES("+iterb+",'"+associate+"');"; + System.out.println("here is sql statement for associate:"+ sql); + stmt.executeUpdate(sql); + iterb++; + } + + + } + + + +} diff --git a/core-java/src/main/java/com/baeldung/jdbcrowset/ExampleListener.java b/core-java/src/main/java/com/baeldung/jdbcrowset/ExampleListener.java new file mode 100644 index 0000000000..7d5bb759f5 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/jdbcrowset/ExampleListener.java @@ -0,0 +1,24 @@ +package com.baeldung.jdbcrowset; + +import javax.sql.RowSetEvent; +import javax.sql.RowSetListener; + +public class ExampleListener implements RowSetListener { + + + public void cursorMoved(RowSetEvent event) { + System.out.println("ExampleListener alerted of cursorMoved event"); + System.out.println(event.toString()); + } + + public void rowChanged(RowSetEvent event) { + System.out.println("ExampleListener alerted of rowChanged event"); + System.out.println(event.toString()); + } + + public void rowSetChanged(RowSetEvent event) { + System.out.println("ExampleListener alerted of rowSetChanged event"); + System.out.println(event.toString()); + } + +} diff --git a/core-java/src/main/java/com/baeldung/jdbcrowset/FilterExample.java b/core-java/src/main/java/com/baeldung/jdbcrowset/FilterExample.java new file mode 100644 index 0000000000..14e738f72d --- /dev/null +++ b/core-java/src/main/java/com/baeldung/jdbcrowset/FilterExample.java @@ -0,0 +1,46 @@ +package com.baeldung.jdbcrowset; + +import java.sql.SQLException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.sql.RowSet; +import javax.sql.rowset.Predicate; + +public class FilterExample implements Predicate { + + private Pattern pattern; + + public FilterExample(String regexQuery) { + if (regexQuery != null && !regexQuery.isEmpty()) { + pattern = Pattern.compile(regexQuery); + } + } + + public boolean evaluate(RowSet rs) { + try { + if (!rs.isAfterLast()) { + String name = rs.getString("name"); + System.out.println(String.format( + "Searching for pattern '%s' in %s", pattern.toString(), + name)); + Matcher matcher = pattern.matcher(name); + return matcher.matches(); + } else + return false; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + public boolean evaluate(Object value, int column) throws SQLException { + throw new UnsupportedOperationException("This operation is unsupported."); + } + + public boolean evaluate(Object value, String columnName) + throws SQLException { + throw new UnsupportedOperationException("This operation is unsupported."); + } + +} diff --git a/core-java/src/main/java/com/baeldung/jdbcrowset/JdbcRowsetApplication.java b/core-java/src/main/java/com/baeldung/jdbcrowset/JdbcRowsetApplication.java new file mode 100644 index 0000000000..72c462ac42 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/jdbcrowset/JdbcRowsetApplication.java @@ -0,0 +1,139 @@ +package com.baeldung.jdbcrowset; + +import java.io.FileOutputStream; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Statement; + +import com.sun.rowset.*; + +import javax.sql.rowset.CachedRowSet; +import javax.sql.rowset.FilteredRowSet; +import javax.sql.rowset.JdbcRowSet; +import javax.sql.rowset.JoinRowSet; +import javax.sql.rowset.RowSetFactory; +import javax.sql.rowset.RowSetProvider; +import javax.sql.rowset.WebRowSet; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class JdbcRowsetApplication { + + public static void main(String[] args) throws Exception { + SpringApplication.run(JdbcRowsetApplication.class, args); + Statement stmt = null; + try { + Connection conn = DatabaseConfiguration.geth2Connection(); + + String drop = "DROP TABLE IF EXISTS customers, associates;"; + String schema = "CREATE TABLE customers (id INT NOT NULL, name VARCHAR(50) NOT NULL, PRIMARY KEY (id)); "; + String schemapartb = "CREATE TABLE associates (id INT NOT NULL, name VARCHAR(50) NOT NULL, PRIMARY KEY (id));"; + + stmt = conn.createStatement(); + stmt.executeUpdate(drop); + stmt.executeUpdate(schema); + stmt.executeUpdate(schemapartb); + // insert data + DatabaseConfiguration.initDatabase(stmt); + // JdbcRowSet Example + String sql = "SELECT * FROM customers"; + JdbcRowSet jdbcRS; + jdbcRS = new JdbcRowSetImpl(conn); + jdbcRS.setType(ResultSet.TYPE_SCROLL_INSENSITIVE); + jdbcRS.setCommand(sql); + jdbcRS.execute(); + jdbcRS.addRowSetListener(new ExampleListener()); + + while (jdbcRS.next()) { + // each call to next, generates a cursorMoved event + System.out.println("id=" + jdbcRS.getString(1)); + System.out.println("name=" + jdbcRS.getString(2)); + } + + // CachedRowSet Example + String username = "sa"; + String password = ""; + String url = "jdbc:h2:mem:testdb"; + CachedRowSet crs = new CachedRowSetImpl(); + crs.setUsername(username); + crs.setPassword(password); + crs.setUrl(url); + crs.setCommand(sql); + crs.execute(); + crs.addRowSetListener(new ExampleListener()); + while (crs.next()) { + if (crs.getInt("id") == 1) { + System.out.println("CRS found customer1 and will remove the record."); + crs.deleteRow(); + break; + } + } + + // WebRowSet example + WebRowSet wrs = new WebRowSetImpl(); + wrs.setUsername(username); + wrs.setPassword(password); + wrs.setUrl(url); + wrs.setCommand(sql); + wrs.execute(); + FileOutputStream ostream = new FileOutputStream("customers.xml"); + wrs.writeXml(ostream); + + // JoinRowSet example + CachedRowSetImpl customers = new CachedRowSetImpl(); + customers.setUsername(username); + customers.setPassword(password); + customers.setUrl(url); + customers.setCommand(sql); + customers.execute(); + + CachedRowSetImpl associates = new CachedRowSetImpl(); + associates.setUsername(username); + associates.setPassword(password); + associates.setUrl(url); + String associatesSQL = "SELECT * FROM associates"; + associates.setCommand(associatesSQL); + associates.execute(); + + JoinRowSet jrs = new JoinRowSetImpl(); + final String ID = "id"; + final String NAME = "name"; + jrs.addRowSet(customers, ID); + jrs.addRowSet(associates, ID); + jrs.last(); + System.out.println("Total rows: " + jrs.getRow()); + jrs.beforeFirst(); + while (jrs.next()) { + + String string1 = jrs.getString(ID); + String string2 = jrs.getString(NAME); + System.out.println("ID: " + string1 + ", NAME: " + string2); + } + + // FilteredRowSet example + RowSetFactory rsf = RowSetProvider.newFactory(); + FilteredRowSet frs = rsf.createFilteredRowSet(); + frs.setCommand("select * from customers"); + frs.execute(conn); + frs.setFilter(new FilterExample("^[A-C].*")); + + ResultSetMetaData rsmd = frs.getMetaData(); + int columncount = rsmd.getColumnCount(); + while (frs.next()) { + for (int i = 1; i <= columncount; i++) { + System.out.println(rsmd.getColumnLabel(i) + " = " + frs.getObject(i) + " "); + } + } + + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + +} diff --git a/core-java/src/test/java/com/baeldung/jdbcrowset/JdbcRowSetTest.java b/core-java/src/test/java/com/baeldung/jdbcrowset/JdbcRowSetTest.java new file mode 100644 index 0000000000..cb455c213a --- /dev/null +++ b/core-java/src/test/java/com/baeldung/jdbcrowset/JdbcRowSetTest.java @@ -0,0 +1,157 @@ +package com.baeldung.jdbcrowset; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Statement; + +import javax.sql.rowset.CachedRowSet; +import javax.sql.rowset.FilteredRowSet; +import javax.sql.rowset.JdbcRowSet; +import javax.sql.rowset.JoinRowSet; +import javax.sql.rowset.RowSetFactory; +import javax.sql.rowset.RowSetProvider; +import javax.sql.rowset.WebRowSet; + +import org.junit.Before; +import org.junit.Test; + +import com.sun.rowset.CachedRowSetImpl; +import com.sun.rowset.JdbcRowSetImpl; +import com.sun.rowset.JoinRowSetImpl; +import com.sun.rowset.WebRowSetImpl; + +public class JdbcRowSetTest { + Statement stmt = null; + String username = "sa"; + String password = ""; + String url = "jdbc:h2:mem:testdb"; + String sql = "SELECT * FROM customers"; + + @Before + public void setup() throws Exception { + Connection conn = DatabaseConfiguration.geth2Connection(); + + String drop = "DROP TABLE IF EXISTS customers, associates;"; + String schema = "CREATE TABLE customers (id INT NOT NULL, name VARCHAR(50) NOT NULL, PRIMARY KEY (id)); "; + String schemapartb = "CREATE TABLE associates (id INT NOT NULL, name VARCHAR(50) NOT NULL, PRIMARY KEY (id));"; + stmt = conn.createStatement(); + stmt.executeUpdate(drop); + stmt.executeUpdate(schema); + stmt.executeUpdate(schemapartb); + DatabaseConfiguration.initDatabase(stmt); + + } + + // JdbcRowSet Example + @Test + public void createJdbcRowSet_SelectCustomers_ThenCorrect() throws Exception { + + String sql = "SELECT * FROM customers"; + JdbcRowSet jdbcRS; + Connection conn = DatabaseConfiguration.geth2Connection(); + jdbcRS = new JdbcRowSetImpl(conn); + jdbcRS.setType(ResultSet.TYPE_SCROLL_INSENSITIVE); + jdbcRS.setCommand(sql); + jdbcRS.execute(); + jdbcRS.addRowSetListener(new ExampleListener()); + + while (jdbcRS.next()) { + // each call to next, generates a cursorMoved event + System.out.println("id=" + jdbcRS.getString(1)); + System.out.println("name=" + jdbcRS.getString(2)); + } + + } + + // CachedRowSet Example + @Test + public void createCachedRowSet_DeleteRecord_ThenCorrect() throws Exception { + + CachedRowSet crs = new CachedRowSetImpl(); + crs.setUsername(username); + crs.setPassword(password); + crs.setUrl(url); + crs.setCommand(sql); + crs.execute(); + crs.addRowSetListener(new ExampleListener()); + while (crs.next()) { + if (crs.getInt("id") == 1) { + System.out.println("CRS found customer1 and will remove the record."); + crs.deleteRow(); + break; + } + } + } + + // WebRowSet example + @Test + public void createWebRowSet_SelectCustomers_WritetoXML_ThenCorrect() throws SQLException, IOException { + + WebRowSet wrs = new WebRowSetImpl(); + wrs.setUsername(username); + wrs.setPassword(password); + wrs.setUrl(url); + wrs.setCommand(sql); + wrs.execute(); + FileOutputStream ostream = new FileOutputStream("customers.xml"); + wrs.writeXml(ostream); + } + + // JoinRowSet example + @Test + public void createCachedRowSets_DoJoinRowSet_ThenCorrect() throws Exception { + + CachedRowSetImpl customers = new CachedRowSetImpl(); + customers.setUsername(username); + customers.setPassword(password); + customers.setUrl(url); + customers.setCommand(sql); + customers.execute(); + + CachedRowSetImpl associates = new CachedRowSetImpl(); + associates.setUsername(username); + associates.setPassword(password); + associates.setUrl(url); + String associatesSQL = "SELECT * FROM associates"; + associates.setCommand(associatesSQL); + associates.execute(); + + JoinRowSet jrs = new JoinRowSetImpl(); + final String ID = "id"; + final String NAME = "name"; + jrs.addRowSet(customers, ID); + jrs.addRowSet(associates, ID); + jrs.last(); + System.out.println("Total rows: " + jrs.getRow()); + jrs.beforeFirst(); + while (jrs.next()) { + + String string1 = jrs.getString(ID); + String string2 = jrs.getString(NAME); + System.out.println("ID: " + string1 + ", NAME: " + string2); + } + } + + // FilteredRowSet example + @Test + public void createFilteredRowSet_filterByRegexExpression_thenCorrect() throws Exception { + RowSetFactory rsf = RowSetProvider.newFactory(); + FilteredRowSet frs = rsf.createFilteredRowSet(); + frs.setCommand("select * from customers"); + Connection conn = DatabaseConfiguration.geth2Connection(); + frs.execute(conn); + frs.setFilter(new FilterExample("^[A-C].*")); + + ResultSetMetaData rsmd = frs.getMetaData(); + int columncount = rsmd.getColumnCount(); + while (frs.next()) { + for (int i = 1; i <= columncount; i++) { + System.out.println(rsmd.getColumnLabel(i) + " = " + frs.getObject(i) + " "); + } + } + } +} From e4d301e81dfbfca03d385975eed121160302939c Mon Sep 17 00:00:00 2001 From: ericgoebelbecker <@592Gbetz> Date: Fri, 1 Dec 2017 15:08:19 -0500 Subject: [PATCH 38/50] BAEL-1374 - search an array --- .../com/baeldung/array/SearchArrayTest.java | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 core-java/src/test/java/com/baeldung/array/SearchArrayTest.java diff --git a/core-java/src/test/java/com/baeldung/array/SearchArrayTest.java b/core-java/src/test/java/com/baeldung/array/SearchArrayTest.java new file mode 100644 index 0000000000..204e8a7945 --- /dev/null +++ b/core-java/src/test/java/com/baeldung/array/SearchArrayTest.java @@ -0,0 +1,116 @@ +package com.baeldung.array; + +import org.junit.Test; + +import java.util.*; + +public class SearchArrayTest { + + + @Test + public void searchArrayAllocNewCollections() { + + int count = 1000; + + String[] array = seedArray(count); + + long startTime = System.nanoTime(); + for (int i = 0; i < count; i++) { + searchList(array, "A"); + } + long duration = System.nanoTime() - startTime; + System.out.println("SearchList: " + duration / 10000); + + startTime = System.nanoTime(); + for (int i = 0; i < count; i++) { + searchSet(array,"A"); + } + duration = System.nanoTime() - startTime; + System.out.println("SearchSet: " + duration / 10000); + + startTime = System.nanoTime(); + for (int i = 0; i < count; i++) { + searchLoop(array, "A"); + } + duration = System.nanoTime() - startTime; + System.out.println("SearchLoop: " + duration / 10000); + } + + @Test + public void searchArrayReuseCollections() { + + int count = 10000; + String[] array = seedArray(count); + + List asList = Arrays.asList(array); + Set asSet = new HashSet<>(Arrays.asList(array)); + + long startTime = System.nanoTime(); + for (int i = 0; i < count; i++) { + asList.contains("A"); + } + long duration = System.nanoTime() - startTime; + System.out.println("List: " + duration / 10000); + + startTime = System.nanoTime(); + for (int i = 0; i < count; i++) { + asSet.contains("A"); + } + duration = System.nanoTime() - startTime; + System.out.println("Set: " + duration / 10000); + + startTime = System.nanoTime(); + for (int i = 0; i < count; i++) { + searchLoop(array, "A"); + } + duration = System.nanoTime() - startTime; + System.out.println("Loop: " + duration / 10000); + + } + + + @Test + public void searchArrayBinarySearch() { + + int count = 10000; + String[] array = seedArray(count); + Arrays.sort(array); + + long startTime = System.nanoTime(); + for (int i = 0; i < count; i++) { + Arrays.binarySearch(array, "A"); + } + long duration = System.nanoTime() - startTime; + System.out.println("Binary search: " + duration / 10000); + + } + + private boolean searchList(String[] arr, String targetValue) { + return Arrays.asList(arr).contains(targetValue); + } + + private boolean searchSet(String[] arr, String targetValue) { + Set set = new HashSet<>(Arrays.asList(arr)); + return set.contains(targetValue); + } + + private boolean searchLoop(String[] arr, String targetValue) { + for (String s : arr) { + if (s.equals(targetValue)) + return true; + } + return true; + } + + private String[] seedArray(int length) { + + String[] strings = new String[length]; + Random random = new Random(); + for (int i = 0; i < length; i++) + { + strings[i] = String.valueOf(random.nextInt()); + } + return strings; + } + +} From 89ab473651138ae2cf2f07881cd7e2550bd64232 Mon Sep 17 00:00:00 2001 From: ericgoebelbecker <@592Gbetz> Date: Fri, 1 Dec 2017 18:02:13 -0500 Subject: [PATCH 39/50] BAEL-1374 - search an array. Name changes. --- .../com/baeldung/array/SearchArrayTest.java | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/core-java/src/test/java/com/baeldung/array/SearchArrayTest.java b/core-java/src/test/java/com/baeldung/array/SearchArrayTest.java index 204e8a7945..94911baac9 100644 --- a/core-java/src/test/java/com/baeldung/array/SearchArrayTest.java +++ b/core-java/src/test/java/com/baeldung/array/SearchArrayTest.java @@ -12,25 +12,25 @@ public class SearchArrayTest { int count = 1000; - String[] array = seedArray(count); + String[] strings = seedArray(count); long startTime = System.nanoTime(); for (int i = 0; i < count; i++) { - searchList(array, "A"); + searchList(strings, "W"); } long duration = System.nanoTime() - startTime; System.out.println("SearchList: " + duration / 10000); startTime = System.nanoTime(); for (int i = 0; i < count; i++) { - searchSet(array,"A"); + searchSet(strings,"S"); } duration = System.nanoTime() - startTime; System.out.println("SearchSet: " + duration / 10000); startTime = System.nanoTime(); for (int i = 0; i < count; i++) { - searchLoop(array, "A"); + searchLoop(strings, "T"); } duration = System.nanoTime() - startTime; System.out.println("SearchLoop: " + duration / 10000); @@ -40,28 +40,28 @@ public class SearchArrayTest { public void searchArrayReuseCollections() { int count = 10000; - String[] array = seedArray(count); + String[] strings = seedArray(count); - List asList = Arrays.asList(array); - Set asSet = new HashSet<>(Arrays.asList(array)); + List asList = Arrays.asList(strings); + Set asSet = new HashSet<>(Arrays.asList(strings)); long startTime = System.nanoTime(); for (int i = 0; i < count; i++) { - asList.contains("A"); + asList.contains("W"); } long duration = System.nanoTime() - startTime; System.out.println("List: " + duration / 10000); startTime = System.nanoTime(); for (int i = 0; i < count; i++) { - asSet.contains("A"); + asSet.contains("S"); } duration = System.nanoTime() - startTime; System.out.println("Set: " + duration / 10000); startTime = System.nanoTime(); for (int i = 0; i < count; i++) { - searchLoop(array, "A"); + searchLoop(strings, "T"); } duration = System.nanoTime() - startTime; System.out.println("Loop: " + duration / 10000); @@ -73,33 +73,33 @@ public class SearchArrayTest { public void searchArrayBinarySearch() { int count = 10000; - String[] array = seedArray(count); - Arrays.sort(array); + String[] strings = seedArray(count); + Arrays.sort(strings); long startTime = System.nanoTime(); for (int i = 0; i < count; i++) { - Arrays.binarySearch(array, "A"); + Arrays.binarySearch(strings, "A"); } long duration = System.nanoTime() - startTime; System.out.println("Binary search: " + duration / 10000); } - private boolean searchList(String[] arr, String targetValue) { - return Arrays.asList(arr).contains(targetValue); + private boolean searchList(String[] strings, String searchString) { + return Arrays.asList(strings).contains(searchString); } - private boolean searchSet(String[] arr, String targetValue) { - Set set = new HashSet<>(Arrays.asList(arr)); - return set.contains(targetValue); + private boolean searchSet(String[] strings, String searchString) { + Set set = new HashSet<>(Arrays.asList(strings)); + return set.contains(searchString); } - private boolean searchLoop(String[] arr, String targetValue) { - for (String s : arr) { - if (s.equals(targetValue)) + private boolean searchLoop(String[] strings, String searchString) { + for (String s : strings) { + if (s.equals(searchString)) return true; } - return true; + return false; } private String[] seedArray(int length) { From 2c7481c35fd171afd19be602793ca143c04bb521 Mon Sep 17 00:00:00 2001 From: Loredana Crusoveanu Date: Sat, 2 Dec 2017 11:55:12 +0200 Subject: [PATCH 40/50] Update README.md --- core-java/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/core-java/README.md b/core-java/README.md index 1feee4126e..8287a21d1e 100644 --- a/core-java/README.md +++ b/core-java/README.md @@ -120,4 +120,5 @@ - [Guide to Java String Pool](http://www.baeldung.com/java-string-pool) - [Copy a File with Java](http://www.baeldung.com/java-copy-file) - [Introduction to Creational Design Patterns](http://www.baeldung.com/creational-design-patterns) +- [Quick Example - Comparator vs Comparable in Java](http://www.baeldung.com/java-comparator-comparable) From 3d0e68b4a9bcc50f286ae9afbb224c38fb9233b7 Mon Sep 17 00:00:00 2001 From: Shouvik Bhattacharya <33756821+shouvikbhattacharya@users.noreply.github.com> Date: Sat, 2 Dec 2017 19:57:28 +0530 Subject: [PATCH 41/50] Guide to HashSet (#3165) --- .../baeldung/collection/WhenUsingHashSet.java | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 core-java/src/test/java/com/baeldung/collection/WhenUsingHashSet.java diff --git a/core-java/src/test/java/com/baeldung/collection/WhenUsingHashSet.java b/core-java/src/test/java/com/baeldung/collection/WhenUsingHashSet.java new file mode 100644 index 0000000000..7dc47ee8c2 --- /dev/null +++ b/core-java/src/test/java/com/baeldung/collection/WhenUsingHashSet.java @@ -0,0 +1,93 @@ +package com.baeldung.collection; + +import java.util.ConcurrentModificationException; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.junit.Assert; +import org.junit.Test; + +public class WhenUsingHashSet { + + @Test + public void whenAddingElement_shouldAddElement() { + Set hashset = new HashSet<>(); + Assert.assertTrue(hashset.add("String Added")); + } + + @Test + public void whenCheckingForElement_shouldSearchForElement() { + Set hashsetContains = new HashSet<>(); + hashsetContains.add("String Added"); + Assert.assertTrue(hashsetContains.contains("String Added")); + } + + @Test + public void whenCheckingTheSizeOfHashSet_shouldReturnThesize() { + Set hashSetSize = new HashSet<>(); + hashSetSize.add("String Added"); + Assert.assertEquals(1, hashSetSize.size()); + } + + @Test + public void whenCheckingForEmptyHashSet_shouldCheckForEmpty() { + Set emptyHashSet = new HashSet<>(); + Assert.assertTrue(emptyHashSet.isEmpty()); + } + + @Test + public void whenRemovingElement_shouldRemoveElement() { + Set removeFromHashSet = new HashSet<>(); + removeFromHashSet.add("String Added"); + Assert.assertTrue(removeFromHashSet.remove("String Added")); + } + + @Test + public void whenClearingHashSet_shouldClearHashSet() { + Set clearHashSet = new HashSet<>(); + clearHashSet.add("String Added"); + clearHashSet.clear(); + Assert.assertTrue(clearHashSet.isEmpty()); + } + + @Test + public void whenIteratingHashSet_shouldIterateHashSet() { + Set hashset = new HashSet<>(); + hashset.add("First"); + hashset.add("Second"); + hashset.add("Third"); + Iterator itr = hashset.iterator(); + while (itr.hasNext()) { + System.out.println(itr.next()); + } + } + + @Test(expected = ConcurrentModificationException.class) + public void whenModifyingHashSetWhileIterating_shouldThrowException() { + Set hashset = new HashSet<>(); + hashset.add("First"); + hashset.add("Second"); + hashset.add("Third"); + Iterator itr = hashset.iterator(); + while (itr.hasNext()) { + itr.next(); + hashset.remove("Second"); + } + } + + @Test + public void whenRemovingElementUsingIterator_shouldRemoveElement() { + Set hashset = new HashSet<>(); + hashset.add("First"); + hashset.add("Second"); + hashset.add("Third"); + Iterator itr = hashset.iterator(); + while (itr.hasNext()) { + String element = itr.next(); + if (element.equals("Second")) + itr.remove(); + } + Assert.assertEquals(2, hashset.size()); + } +} From 79cf459013a258294192a0b96af5b66e2407c977 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Walter=20G=C3=B3mez?= Date: Sat, 2 Dec 2017 10:04:30 -0600 Subject: [PATCH 42/50] Generated id fix (#3180) * Add project for hibernate immutable article Add Event entity Add hibernate configuration file Add hibernateutil for configuration Add test to match snippets from article * Update master * Update hibernate tests Change sesssion handling Modify event creation * Remove setId method on generated id --- .../hibernate/immutable/HibernateImmutableIntegrationTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/immutable/HibernateImmutableIntegrationTest.java b/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/immutable/HibernateImmutableIntegrationTest.java index d6ecdb29d6..c5a7633c74 100644 --- a/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/immutable/HibernateImmutableIntegrationTest.java +++ b/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/immutable/HibernateImmutableIntegrationTest.java @@ -115,7 +115,6 @@ public class HibernateImmutableIntegrationTest { private static void createEventGenerated() { EventGeneratedId eventGeneratedId = new EventGeneratedId("John", "Doe"); - eventGeneratedId.setId(4L); session.save(eventGeneratedId); } From c2e8eb2ccccb744e8d6cd9c0f1c490dfc66c032b Mon Sep 17 00:00:00 2001 From: Eugen Paraschiv Date: Sat, 2 Dec 2017 18:24:28 +0200 Subject: [PATCH 43/50] cleanup Hibernate work --- .../baeldung/spring/PersistenceConfig.java | 2 +- .../src/main/resources/manytomany.cfg.xml | 24 +++++-------------- .../HibernateImmutableIntegrationTest.java | 2 +- 3 files changed, 8 insertions(+), 20 deletions(-) diff --git a/persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/spring/PersistenceConfig.java b/persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/spring/PersistenceConfig.java index 74ac0a269e..e64f0a4efe 100644 --- a/persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/spring/PersistenceConfig.java +++ b/persistence-modules/spring-hibernate-5/src/main/java/com/baeldung/spring/PersistenceConfig.java @@ -21,7 +21,7 @@ import java.util.Properties; @Configuration @EnableTransactionManagement -@PropertySource({ "classpath:persistence-h2.properties" }) +@PropertySource({ "classpath:persistence-mysql.properties" }) @ComponentScan({ "com.baeldung.persistence" }) public class PersistenceConfig { diff --git a/persistence-modules/spring-hibernate-5/src/main/resources/manytomany.cfg.xml b/persistence-modules/spring-hibernate-5/src/main/resources/manytomany.cfg.xml index 8a10fc1580..3c753a89af 100644 --- a/persistence-modules/spring-hibernate-5/src/main/resources/manytomany.cfg.xml +++ b/persistence-modules/spring-hibernate-5/src/main/resources/manytomany.cfg.xml @@ -4,24 +4,12 @@ "http://hibernate.org/dtd/hibernate-configuration-3.0.dtd"> - - com.mysql.jdbc.Driver - - - buddhinisam123 - - - jdbc:mysql://localhost:3306/spring_hibernate_many_to_many - - - root - - - org.hibernate.dialect.MySQLDialect - - - thread - + com.mysql.jdbc.Driver + tutorialmy5ql + jdbc:mysql://localhost:3306/spring_hibernate_many_to_many + tutorialuser + org.hibernate.dialect.MySQLDialect + thread true diff --git a/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/immutable/HibernateImmutableIntegrationTest.java b/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/immutable/HibernateImmutableIntegrationTest.java index c5a7633c74..8465dee21c 100644 --- a/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/immutable/HibernateImmutableIntegrationTest.java +++ b/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/immutable/HibernateImmutableIntegrationTest.java @@ -4,7 +4,6 @@ import com.baeldung.hibernate.immutable.entities.Event; import com.baeldung.hibernate.immutable.entities.EventGeneratedId; import com.baeldung.hibernate.immutable.util.HibernateUtil; import com.google.common.collect.Sets; -import org.hibernate.CacheMode; import org.hibernate.Session; import org.junit.*; import org.junit.rules.ExpectedException; @@ -98,6 +97,7 @@ public class HibernateImmutableIntegrationTest { public void updateEventGenerated() { createEventGenerated(); EventGeneratedId eventGeneratedId = (EventGeneratedId) session.createQuery("FROM EventGeneratedId WHERE name LIKE '%John%'").list().get(0); + eventGeneratedId.setName("Mike"); session.update(eventGeneratedId); session.flush(); From 9f54deaff764051a6d758ea5716f77dd3381008f Mon Sep 17 00:00:00 2001 From: Eugen Paraschiv Date: Sat, 2 Dec 2017 18:26:00 +0200 Subject: [PATCH 44/50] minor documentation work --- .../hibernate/immutable/HibernateImmutableIntegrationTest.java | 3 +++ .../HibernateManyToManyAnnotationMainIntegrationTest.java | 3 +++ 2 files changed, 6 insertions(+) diff --git a/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/immutable/HibernateImmutableIntegrationTest.java b/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/immutable/HibernateImmutableIntegrationTest.java index 8465dee21c..1572f23ed1 100644 --- a/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/immutable/HibernateImmutableIntegrationTest.java +++ b/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/immutable/HibernateImmutableIntegrationTest.java @@ -13,6 +13,9 @@ import javax.persistence.PersistenceException; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.IsEqual.equalTo; +/** + * Configured in: immutable.cfg.xml + */ public class HibernateImmutableIntegrationTest { private static Session session; diff --git a/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/manytomany/HibernateManyToManyAnnotationMainIntegrationTest.java b/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/manytomany/HibernateManyToManyAnnotationMainIntegrationTest.java index 9a536a0f80..0073e181cc 100644 --- a/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/manytomany/HibernateManyToManyAnnotationMainIntegrationTest.java +++ b/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/manytomany/HibernateManyToManyAnnotationMainIntegrationTest.java @@ -17,6 +17,9 @@ import com.baeldung.hibernate.manytomany.util.HibernateUtil; import com.baeldung.hibernate.manytomany.model.Employee; import com.baeldung.hibernate.manytomany.model.Project; +/** + * Configured in: manytomany.cfg.xml + */ public class HibernateManyToManyAnnotationMainIntegrationTest { private static SessionFactory sessionFactory; From 4e39bfb945ddd83f208a193c527a32787888b036 Mon Sep 17 00:00:00 2001 From: Shouvik Bhattacharya <33756821+shouvikbhattacharya@users.noreply.github.com> Date: Sun, 3 Dec 2017 02:24:36 +0530 Subject: [PATCH 45/50] Bael-1305: A Guide to Java Loops (#3181) * BAEL-1305: A Simple Guide to Java Loops. * BAEL-1305: Updated article to add few more examples. --- .../com/baeldung/loops/WhenUsingLoops.java | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/core-java/src/test/java/com/baeldung/loops/WhenUsingLoops.java b/core-java/src/test/java/com/baeldung/loops/WhenUsingLoops.java index 9590eabfef..f82f9ddaa7 100644 --- a/core-java/src/test/java/com/baeldung/loops/WhenUsingLoops.java +++ b/core-java/src/test/java/com/baeldung/loops/WhenUsingLoops.java @@ -1,11 +1,38 @@ package com.baeldung.loops; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + import org.junit.Assert; +import org.junit.BeforeClass; import org.junit.Test; public class WhenUsingLoops { private LoopsInJava loops = new LoopsInJava(); + private static List list = new ArrayList<>(); + private static Set set = new HashSet<>(); + private static Map map = new HashMap<>(); + + @BeforeClass + public static void setUp() { + list.add("One"); + list.add("Two"); + list.add("Three"); + + set.add("Four"); + set.add("Five"); + set.add("Six"); + + map.put("One", 1); + map.put("Two", 2); + map.put("Three", 3); + } @Test public void shouldRunForLoop() { @@ -34,4 +61,47 @@ public class WhenUsingLoops { int[] actual = loops.do_while_loop(); Assert.assertArrayEquals(expected, actual); } + + @Test + public void whenUsingSimpleFor_shouldIterateList() { + for (int i = 0; i < list.size(); i++) { + System.out.println(list.get(i)); + } + } + + @Test + public void whenUsingEnhancedFor_shouldIterateList() { + for (String item : list) { + System.out.println(item); + } + } + + @Test + public void whenUsingEnhancedFor_shouldIterateSet() { + for (String item : set) { + System.out.println(item); + } + } + + @Test + public void whenUsingEnhancedFor_shouldIterateMap() { + for (Entry entry : map.entrySet()) { + System.out.println("Key: " + entry.getKey() + " - " + "Value: " + entry.getValue()); + } + } + + @Test + public void whenUsingSimpleFor_shouldRunLabelledLoop() { + aa: for (int i = 1; i <= 3; i++) { + if (i == 1) + continue; + bb: for (int j = 1; j <= 3; j++) { + if (i == 2 && j == 2) { + break aa; + } + System.out.println(i + " " + j); + } + } + } + } From c01c2266b839a8791c8a9cc62e3670655bb83a90 Mon Sep 17 00:00:00 2001 From: Yasin Date: Sun, 3 Dec 2017 16:51:42 +0530 Subject: [PATCH 46/50] BAEL-1334: Guide to Hibernate Spatial (#3183) * BAEL-1334 Guide to Hibernate Spatial * BAEL-1334 Guide to Hibernate Spatial Moving the files to hibernate5 from libraries * Reverting the pom file * BAEL-1334 Guide to Hibernate Spatial * BAEL-1334 Guide to Hibernate Spatial Moving the files to hibernate5 from libraries * Reverting the pom file * BAEL-1334 Guide to Hibernate Spatial Improved assertions --- .../java/com/baeldung/hibernate/HibernateSpatialTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hibernate5/src/test/java/com/baeldung/hibernate/HibernateSpatialTest.java b/hibernate5/src/test/java/com/baeldung/hibernate/HibernateSpatialTest.java index 6d0aa0a4cd..a5c7b329fc 100644 --- a/hibernate5/src/test/java/com/baeldung/hibernate/HibernateSpatialTest.java +++ b/hibernate5/src/test/java/com/baeldung/hibernate/HibernateSpatialTest.java @@ -14,7 +14,9 @@ import org.junit.Test; import javax.persistence.Query; import java.io.IOException; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; + import static org.junit.Assert.assertTrue; public class HibernateSpatialTest { @@ -76,10 +78,8 @@ public class HibernateSpatialTest { Query query = session.createQuery("select p from PointEntity p where within(p.point, :area) = true", PointEntity.class); query.setParameter("area", wktToGeometry("POLYGON((0 0, 0 5, 5 5, 5 0, 0 0))")); - assertEquals(3, query.getResultList().size()); - assertEquals("POINT (1 1)", ((PointEntity) query.getResultList().get(0)).getPoint().toString()); - assertEquals("POINT (1 2)", ((PointEntity) query.getResultList().get(1)).getPoint().toString()); - assertEquals("POINT (3 4)", ((PointEntity) query.getResultList().get(2)).getPoint().toString()); + assertThat(query.getResultList().stream().map(p -> ((PointEntity) p).getPoint().toString())) + .containsOnly("POINT (1 1)", "POINT (1 2)", "POINT (3 4)"); } private void insertPoint(String point) throws ParseException { From 63d4e7d0143404684fec3b27db4931100a7c929c Mon Sep 17 00:00:00 2001 From: Muhammed Almas Date: Mon, 4 Dec 2017 01:52:39 +0530 Subject: [PATCH 47/50] BAEL-1375 Counter in Java. (#3170) * BAEL-1375 Counter in Java. * BAEL-1375 Frequency counter in java. * BAEL-1375 Refactored method names. --- core-java-8/pom.xml | 532 +++++++++--------- .../baeldung/counter/CounterStatistics.java | 45 ++ .../com/baeldung/counter/CounterTest.java | 53 ++ .../com/baeldung/counter/CounterUtil.java | 61 ++ 4 files changed, 434 insertions(+), 257 deletions(-) create mode 100644 core-java-8/src/test/java/com/baeldung/counter/CounterStatistics.java create mode 100644 core-java-8/src/test/java/com/baeldung/counter/CounterTest.java create mode 100644 core-java-8/src/test/java/com/baeldung/counter/CounterUtil.java diff --git a/core-java-8/pom.xml b/core-java-8/pom.xml index f5506f095e..17d330b3b8 100644 --- a/core-java-8/pom.xml +++ b/core-java-8/pom.xml @@ -1,258 +1,276 @@ - - 4.0.0 - com.baeldung - core-java-8 - 0.1.0-SNAPSHOT - jar - - core-java-8 - - - - com.baeldung - parent-modules - 1.0.0-SNAPSHOT - - - - - - - com.google.guava - guava - ${guava.version} - - - - org.apache.commons - commons-collections4 - ${commons-collections4.version} - - - - commons-io - commons-io - ${commons-io.version} - - - - org.apache.commons - commons-lang3 - ${commons-lang3.version} - - - - org.apache.commons - commons-math3 - ${commons-math3.version} - - - - log4j - log4j - 1.2.17 - - - - commons-codec - commons-codec - ${commons-codec.version} - - - - org.projectlombok - lombok - ${lombok.version} - provided - - - - - - org.assertj - assertj-core - ${assertj.version} - test - - - - com.jayway.awaitility - awaitility - ${avaitility.version} - test - - - - - - core-java-8 - - - src/main/resources - true - - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - copy-dependencies - prepare-package - - copy-dependencies - - - ${project.build.directory}/libs - - - - - - - org.apache.maven.plugins - maven-jar-plugin - - - - true - libs/ - org.baeldung.executable.ExecutableMavenJar - - - - - - org.apache.maven.plugins - maven-assembly-plugin - - - package - - single - - - ${project.basedir} - - - org.baeldung.executable.ExecutableMavenJar - - - - jar-with-dependencies - - - - - - - org.apache.maven.plugins - maven-shade-plugin - - - - shade - - - true - - - org.baeldung.executable.ExecutableMavenJar - - - - - - - - com.jolira - onejar-maven-plugin - - - - org.baeldung.executable.ExecutableMavenJar - true - ${project.build.finalName}-onejar.${project.packaging} - - - one-jar - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - repackage - - - spring-boot - org.baeldung.executable.ExecutableMavenJar - - - - - - - - - - - integration - - - - org.apache.maven.plugins - maven-surefire-plugin - - - integration-test - - test - - - - **/*ManualTest.java - - - **/*IntegrationTest.java - - - - - - - json - - - - - - - - - - - - 21.0 - 3.5 - 3.6.1 - 2.5 - 4.1 - 4.01 - 1.10 - 1.16.12 - - - 3.6.1 - 1.7.0 - - + + 4.0.0 + com.baeldung + core-java-8 + 0.1.0-SNAPSHOT + jar + + core-java-8 + + + + com.baeldung + parent-modules + 1.0.0-SNAPSHOT + + + + + + + com.google.guava + guava + ${guava.version} + + + + org.apache.commons + commons-collections4 + ${commons-collections4.version} + + + + commons-io + commons-io + ${commons-io.version} + + + + org.apache.commons + commons-lang3 + ${commons-lang3.version} + + + + org.apache.commons + commons-math3 + ${commons-math3.version} + + + + log4j + log4j + 1.2.17 + + + + commons-codec + commons-codec + ${commons-codec.version} + + + + org.projectlombok + lombok + ${lombok.version} + provided + + + + + + org.assertj + assertj-core + ${assertj.version} + test + + + + com.jayway.awaitility + awaitility + ${avaitility.version} + test + + + + org.openjdk.jmh + jmh-core + 1.19 + + + + org.openjdk.jmh + jmh-generator-annprocess + 1.19 + + + + org.openjdk.jmh + jmh-generator-bytecode + 1.19 + + + + + + core-java-8 + + + src/main/resources + true + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-dependencies + prepare-package + + copy-dependencies + + + ${project.build.directory}/libs + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + true + libs/ + org.baeldung.executable.ExecutableMavenJar + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + package + + single + + + ${project.basedir} + + + org.baeldung.executable.ExecutableMavenJar + + + + jar-with-dependencies + + + + + + + org.apache.maven.plugins + maven-shade-plugin + + + + shade + + + true + + + org.baeldung.executable.ExecutableMavenJar + + + + + + + + com.jolira + onejar-maven-plugin + + + + org.baeldung.executable.ExecutableMavenJar + true + ${project.build.finalName}-onejar.${project.packaging} + + + one-jar + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + spring-boot + org.baeldung.executable.ExecutableMavenJar + + + + + + + + + + + integration + + + + org.apache.maven.plugins + maven-surefire-plugin + + + integration-test + + test + + + + **/*ManualTest.java + + + **/*IntegrationTest.java + + + + + + + json + + + + + + + + + + + + 21.0 + 3.5 + 3.6.1 + 2.5 + 4.1 + 4.01 + 1.10 + 1.16.12 + + + 3.6.1 + 1.7.0 + + \ No newline at end of file diff --git a/core-java-8/src/test/java/com/baeldung/counter/CounterStatistics.java b/core-java-8/src/test/java/com/baeldung/counter/CounterStatistics.java new file mode 100644 index 0000000000..2a42a166fa --- /dev/null +++ b/core-java-8/src/test/java/com/baeldung/counter/CounterStatistics.java @@ -0,0 +1,45 @@ +package com.baeldung.counter; + +import java.util.HashMap; +import java.util.Map; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Mode; + +import com.baeldung.counter.CounterUtil.MutableInteger; + +@Fork(value = 1, warmups = 3) +@BenchmarkMode(Mode.All) +public class CounterStatistics { + + private static final Map counterMap = new HashMap<>(); + private static final Map counterWithMutableIntMap = new HashMap<>(); + private static final Map counterWithIntArrayMap = new HashMap<>(); + private static final Map counterWithLongWrapperMap = new HashMap<>(); + + @Benchmark + public void wrapperAsCounter() { + CounterUtil.counterWithWrapperObject(counterMap); + } + + @Benchmark + public void lambdaExpressionWithWrapper() { + CounterUtil.counterWithLambdaAndWrapper(counterWithLongWrapperMap); + } + + @Benchmark + public void mutableIntegerAsCounter() { + CounterUtil.counterWithMutableInteger(counterWithMutableIntMap); + } + + @Benchmark + public void primitiveArrayAsCounter() { + CounterUtil.counterWithPrimitiveArray(counterWithIntArrayMap); + } + + public static void main(String[] args) throws Exception { + org.openjdk.jmh.Main.main(args); + } +} diff --git a/core-java-8/src/test/java/com/baeldung/counter/CounterTest.java b/core-java-8/src/test/java/com/baeldung/counter/CounterTest.java new file mode 100644 index 0000000000..657b510452 --- /dev/null +++ b/core-java-8/src/test/java/com/baeldung/counter/CounterTest.java @@ -0,0 +1,53 @@ +package com.baeldung.counter; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import com.baeldung.counter.CounterUtil.MutableInteger; + +public class CounterTest { + + @Test + public void whenMapWithWrapperAsCounter_runsSuccessfully() { + Map counterMap = new HashMap<>(); + CounterUtil.counterWithWrapperObject(counterMap); + + assertEquals(3, counterMap.get("China") + .intValue()); + assertEquals(2, counterMap.get("India") + .intValue()); + } + + @Test + public void whenMapWithLambdaAndWrapperCounter_runsSuccessfully() { + Map counterMap = new HashMap<>(); + CounterUtil.counterWithLambdaAndWrapper(counterMap); + + assertEquals(3l, counterMap.get("China") + .longValue()); + assertEquals(2l, counterMap.get("India") + .longValue()); + } + + @Test + public void whenMapWithMutableIntegerCounter_runsSuccessfully() { + Map counterMap = new HashMap<>(); + CounterUtil.counterWithMutableInteger(counterMap); + assertEquals(3, counterMap.get("China") + .getCount()); + assertEquals(2, counterMap.get("India") + .getCount()); + } + + @Test + public void whenMapWithPrimitiveArray_runsSuccessfully() { + Map counterMap = new HashMap<>(); + CounterUtil.counterWithPrimitiveArray(counterMap); + assertEquals(3, counterMap.get("China")[0]); + assertEquals(2, counterMap.get("India")[0]); + } +} diff --git a/core-java-8/src/test/java/com/baeldung/counter/CounterUtil.java b/core-java-8/src/test/java/com/baeldung/counter/CounterUtil.java new file mode 100644 index 0000000000..647fbfb0cc --- /dev/null +++ b/core-java-8/src/test/java/com/baeldung/counter/CounterUtil.java @@ -0,0 +1,61 @@ +package com.baeldung.counter; + +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class CounterUtil { + + private final static String[] COUNTRY_NAMES = { "China", "Australia", "India", "USA", "USSR", "UK", "China", "France", "Poland", "Austria", "India", "USA", "Egypt", "China" }; + + public static void counterWithWrapperObject(Map counterMap) { + for (String country : COUNTRY_NAMES) { + counterMap.compute(country, (k, v) -> v == null ? 1 : v + 1); + } + } + + public static void counterWithLambdaAndWrapper(Map counterMap) { + counterMap.putAll(Stream.of(COUNTRY_NAMES) + .parallel() + .collect(Collectors.groupingBy(k -> k, Collectors.counting()))); + } + + public static class MutableInteger { + int count; + + public MutableInteger(int count) { + this.count = count; + } + + public void increment() { + this.count++; + } + + public int getCount() { + return this.count; + } + } + + public static void counterWithMutableInteger(Map counterMap) { + for (String country : COUNTRY_NAMES) { + MutableInteger oldValue = counterMap.get(country); + if (oldValue != null) { + oldValue.increment(); + } else { + counterMap.put(country, new MutableInteger(1)); + } + } + } + + public static void counterWithPrimitiveArray(Map counterMap) { + for (String country : COUNTRY_NAMES) { + int[] oldCounter = counterMap.get(country); + if (oldCounter != null) { + oldCounter[0] += 1; + } else { + counterMap.put(country, new int[] { 1 }); + } + } + } + +} From 7f23c45ef4e747f460f1d1e2f6890be11d4e7da5 Mon Sep 17 00:00:00 2001 From: Taylor Daugherty Date: Mon, 4 Dec 2017 01:40:21 -0500 Subject: [PATCH 48/50] [BAEL-1300] Custom Logback Appender (#3193) * BAEL-1300 Custom logback appender * BAEL-1300 fix to logback module * relativePath --- libraries/helloWorld.docx | Bin 0 -> 76895 bytes logging-modules/logback/README.md | 0 logging-modules/logback/pom.xml | 35 ++++++++++ .../java/com/baeldung/logback/Example.java | 14 ++++ .../com/baeldung/logback/MapAppender.java | 37 +++++++++++ .../logback/src/main/resources/logback.xml | 18 ++++++ .../logback/MapAppenderIntegrationTest.java | 33 ++++++++++ .../com/baeldung/logback/MapAppenderTest.java | 60 ++++++++++++++++++ .../src/test/resources/logback-test.xml | 14 ++++ pom.xml | 1 + xml/src/test/resources/example_dom4j_new.xml | 10 +++ xml/src/test/resources/example_jaxb_new.xml | 9 +++ 12 files changed, 231 insertions(+) create mode 100644 libraries/helloWorld.docx create mode 100644 logging-modules/logback/README.md create mode 100644 logging-modules/logback/pom.xml create mode 100644 logging-modules/logback/src/main/java/com/baeldung/logback/Example.java create mode 100644 logging-modules/logback/src/main/java/com/baeldung/logback/MapAppender.java create mode 100644 logging-modules/logback/src/main/resources/logback.xml create mode 100644 logging-modules/logback/src/test/java/com/baeldung/logback/MapAppenderIntegrationTest.java create mode 100644 logging-modules/logback/src/test/java/com/baeldung/logback/MapAppenderTest.java create mode 100644 logging-modules/logback/src/test/resources/logback-test.xml create mode 100644 xml/src/test/resources/example_dom4j_new.xml create mode 100644 xml/src/test/resources/example_jaxb_new.xml diff --git a/libraries/helloWorld.docx b/libraries/helloWorld.docx new file mode 100644 index 0000000000000000000000000000000000000000..09e71a4d4ec07142de51e5d046b0ac8326d7e7f5 GIT binary patch literal 76895 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�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 Gfd2u|YeTgF literal 0 HcmV?d00001 diff --git a/logging-modules/logback/README.md b/logging-modules/logback/README.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/logging-modules/logback/pom.xml b/logging-modules/logback/pom.xml new file mode 100644 index 0000000000..8169134442 --- /dev/null +++ b/logging-modules/logback/pom.xml @@ -0,0 +1,35 @@ + + + + 4.0.0 + + logback + logback + 0.1-SNAPSHOT + + + UTF-8 + 1.2.3 + + + + com.baeldung + parent-modules + 1.0.0-SNAPSHOT + ../../ + + + + + + ch.qos.logback + logback-classic + ${logback.version} + + + + + + diff --git a/logging-modules/logback/src/main/java/com/baeldung/logback/Example.java b/logging-modules/logback/src/main/java/com/baeldung/logback/Example.java new file mode 100644 index 0000000000..e3d09dc321 --- /dev/null +++ b/logging-modules/logback/src/main/java/com/baeldung/logback/Example.java @@ -0,0 +1,14 @@ +package com.baeldung.logback; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Example { + + private static final Logger logger = LoggerFactory.getLogger(Example.class); + + public static void main(String[] args) { + logger.info("Example log from {}", Example.class.getSimpleName()); + } + +} diff --git a/logging-modules/logback/src/main/java/com/baeldung/logback/MapAppender.java b/logging-modules/logback/src/main/java/com/baeldung/logback/MapAppender.java new file mode 100644 index 0000000000..99cc6488e5 --- /dev/null +++ b/logging-modules/logback/src/main/java/com/baeldung/logback/MapAppender.java @@ -0,0 +1,37 @@ +package com.baeldung.logback; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; + +import java.util.HashMap; +import java.util.Map; + +public class MapAppender extends AppenderBase { + + private final Map eventMap = new HashMap<>(); + + private String prefix; + + @Override + protected void append(final ILoggingEvent event) { + if (prefix == null || "".equals(prefix)) { + addError("Prefix is not set for MapAppender."); + return; + } + + eventMap.put(prefix + System.currentTimeMillis(), event); + } + + public String getPrefix() { + return prefix; + } + + public void setPrefix(final String prefix) { + this.prefix = prefix; + } + + public Map getEventMap() { + return eventMap; + } + +} diff --git a/logging-modules/logback/src/main/resources/logback.xml b/logging-modules/logback/src/main/resources/logback.xml new file mode 100644 index 0000000000..37ae2adbb0 --- /dev/null +++ b/logging-modules/logback/src/main/resources/logback.xml @@ -0,0 +1,18 @@ + + + + test + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + \ No newline at end of file diff --git a/logging-modules/logback/src/test/java/com/baeldung/logback/MapAppenderIntegrationTest.java b/logging-modules/logback/src/test/java/com/baeldung/logback/MapAppenderIntegrationTest.java new file mode 100644 index 0000000000..20366a229d --- /dev/null +++ b/logging-modules/logback/src/test/java/com/baeldung/logback/MapAppenderIntegrationTest.java @@ -0,0 +1,33 @@ +package com.baeldung.logback; + +import ch.qos.logback.classic.Logger; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.LoggerFactory; + +import static org.junit.Assert.assertEquals; + +public class MapAppenderIntegrationTest { + + private Logger rootLogger; + + @Before + public void setUp() throws Exception { + rootLogger = (Logger) LoggerFactory.getLogger("ROOT"); + } + + @Test + public void whenLoggerEmitsLoggingEvent_thenAppenderReceivesEvent() throws Exception { + rootLogger.info("Test from {}", this.getClass().getSimpleName()); + MapAppender appender = (MapAppender) rootLogger.getAppender("map"); + assertEquals(appender.getEventMap().size(), 1); + } + + @Test + public void givenNoPrefixSet_whenLoggerEmitsEvent_thenAppenderReceivesNoEvent() throws Exception { + rootLogger.info("Test from {}", this.getClass().getSimpleName()); + MapAppender appender = (MapAppender) rootLogger.getAppender("badMap"); + assertEquals(appender.getEventMap().size(), 0); + } + +} diff --git a/logging-modules/logback/src/test/java/com/baeldung/logback/MapAppenderTest.java b/logging-modules/logback/src/test/java/com/baeldung/logback/MapAppenderTest.java new file mode 100644 index 0000000000..a5a938a923 --- /dev/null +++ b/logging-modules/logback/src/test/java/com/baeldung/logback/MapAppenderTest.java @@ -0,0 +1,60 @@ +package com.baeldung.logback; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.core.BasicStatusManager; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class MapAppenderTest { + + private LoggerContext ctx; + + private MapAppender mapAppender = new MapAppender(); + + private LoggingEvent event; + + @Before + public void setUp() throws Exception { + ctx = new LoggerContext(); + ctx.setName("test context"); + ctx.setStatusManager(new BasicStatusManager()); + mapAppender.setContext(ctx); + mapAppender.setPrefix("prefix"); + event = new LoggingEvent("fqcn", ctx.getLogger("logger"), Level.INFO, "Test message for logback appender", null, new Object[0]); + ctx.start(); + } + + @After + public void tearDown() throws Exception { + ctx.stop(); + mapAppender.stop(); + } + + @Test + public void whenPrefixIsNull_thenMapAppenderDoesNotLog() throws Exception { + mapAppender.setPrefix(null); + mapAppender.append(event); + assertTrue(mapAppender.getEventMap().isEmpty()); + } + + @Test + public void whenPrefixIsEmpty_thenMapAppenderDoesNotLog() throws Exception { + mapAppender.setPrefix(""); + mapAppender.append(event); + assertTrue(mapAppender.getEventMap().isEmpty()); + } + + @Test + public void whenLogMessageIsEmitted_thenMapAppenderReceivesMessage() throws Exception { + mapAppender.append(event); + assertEquals(mapAppender.getEventMap().size(), 1); + mapAppender.getEventMap().forEach((k, v) -> assertTrue(k.startsWith("prefix"))); + } + +} diff --git a/logging-modules/logback/src/test/resources/logback-test.xml b/logging-modules/logback/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..8254e6ac80 --- /dev/null +++ b/logging-modules/logback/src/test/resources/logback-test.xml @@ -0,0 +1,14 @@ + + + + test + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index cac0cc5845..c8fa30e8c9 100644 --- a/pom.xml +++ b/pom.xml @@ -107,6 +107,7 @@ logging-modules/log-mdc logging-modules/log4j logging-modules/log4j2 + logging-modules/logback lombok mapstruct diff --git a/xml/src/test/resources/example_dom4j_new.xml b/xml/src/test/resources/example_dom4j_new.xml new file mode 100644 index 0000000000..020760fdd3 --- /dev/null +++ b/xml/src/test/resources/example_dom4j_new.xml @@ -0,0 +1,10 @@ + + + + + XML with Dom4J + XML handling with Dom4J + 14/06/2016 + Dom4J tech writer + + diff --git a/xml/src/test/resources/example_jaxb_new.xml b/xml/src/test/resources/example_jaxb_new.xml new file mode 100644 index 0000000000..646d938869 --- /dev/null +++ b/xml/src/test/resources/example_jaxb_new.xml @@ -0,0 +1,9 @@ + + + + Jaxb author + 04/02/2015 + XML Binding with Jaxb + XML with Jaxb + + From 059e66da30976ea19b8cb52030a7743d239b0f6f Mon Sep 17 00:00:00 2001 From: Jose Carvajal Date: Mon, 4 Dec 2017 08:53:58 +0100 Subject: [PATCH 49/50] BAEL-1174: A Quick Guide to Spring Cloud Consul: Changes after senior code review --- .../discovery/DiscoveryClientApplication.java | 45 +---------------- .../discovery/DiscoveryClientController.java | 50 +++++++++++++++++++ .../health/ServiceDiscoveryApplication.java | 15 ------ .../health/ServiceDiscoveryController.java | 22 ++++++++ .../DistributedPropertiesApplication.java | 20 +------- .../DistributedPropertiesController.java | 27 ++++++++++ .../cloud/consul/properties/MyProperties.java | 2 + .../ribbon/RibbonClientApplication.java | 47 ----------------- 8 files changed, 103 insertions(+), 125 deletions(-) create mode 100644 spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/discovery/DiscoveryClientController.java create mode 100644 spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/health/ServiceDiscoveryController.java create mode 100644 spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/properties/DistributedPropertiesController.java delete mode 100644 spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/ribbon/RibbonClientApplication.java diff --git a/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/discovery/DiscoveryClientApplication.java b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/discovery/DiscoveryClientApplication.java index b4f42219a4..d013969ad3 100644 --- a/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/discovery/DiscoveryClientApplication.java +++ b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/discovery/DiscoveryClientApplication.java @@ -1,59 +1,16 @@ package com.baeldung.spring.cloud.consul.discovery; -import java.net.URI; - -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; -import org.springframework.context.annotation.Bean; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.client.RestTemplate; @SpringBootApplication @EnableDiscoveryClient -@RestController public class DiscoveryClientApplication { - @Bean - public RestTemplate restTemplate() { - return new RestTemplate(); - } - - @Autowired - private DiscoveryClient discoveryClient; - - @GetMapping("/discoveryClient") - public String home() { - return restTemplate().getForEntity(serviceUrl().resolve("/ping"), String.class) - .getBody(); - } - - @GetMapping("/ping") - public String ping() { - return "pong"; - } - - @RequestMapping("/my-health-check") - public ResponseEntity myCustomCheck() { - return new ResponseEntity<>(HttpStatus.OK); - } - - public URI serviceUrl() { - return discoveryClient.getInstances("myApp") - .stream() - .findFirst() - .map(si -> si.getUri()) - .orElse(null); - } - public static void main(String[] args) { new SpringApplicationBuilder(DiscoveryClientApplication.class).web(true) .run(args); } + } diff --git a/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/discovery/DiscoveryClientController.java b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/discovery/DiscoveryClientController.java new file mode 100644 index 0000000000..1436096d10 --- /dev/null +++ b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/discovery/DiscoveryClientController.java @@ -0,0 +1,50 @@ +package com.baeldung.spring.cloud.consul.discovery; + +import java.net.URI; +import java.util.Optional; + +import javax.naming.ServiceUnavailableException; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; + +@RestController +public class DiscoveryClientController { + + @Autowired + private DiscoveryClient discoveryClient; + + private final RestTemplate restTemplate = new RestTemplate(); + + @GetMapping("/discoveryClient") + public String discoveryPing() throws RestClientException, ServiceUnavailableException { + URI service = serviceUrl().map(s -> s.resolve("/ping")) + .orElseThrow(ServiceUnavailableException::new); + return restTemplate.getForEntity(service, String.class) + .getBody(); + } + + @GetMapping("/ping") + public String ping() { + return "pong"; + } + + @GetMapping("/my-health-check") + public ResponseEntity myCustomCheck() { + return new ResponseEntity<>(HttpStatus.OK); + } + + public Optional serviceUrl() { + return discoveryClient.getInstances("myApp") + .stream() + .findFirst() + .map(si -> si.getUri()); + } + +} diff --git a/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/health/ServiceDiscoveryApplication.java b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/health/ServiceDiscoveryApplication.java index 7db361eb4f..020d7d017c 100644 --- a/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/health/ServiceDiscoveryApplication.java +++ b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/health/ServiceDiscoveryApplication.java @@ -2,24 +2,9 @@ package com.baeldung.spring.cloud.consul.health; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; @SpringBootApplication -@RestController public class ServiceDiscoveryApplication { - @RequestMapping("/my-health-check") - public ResponseEntity myCustomCheck() { - String message = "Testing my healh check function"; - return new ResponseEntity<>(message, HttpStatus.FORBIDDEN); - } - - @RequestMapping("/ping") - public String ping() { - return "pong"; - } public static void main(String[] args) { new SpringApplicationBuilder(ServiceDiscoveryApplication.class).web(true) diff --git a/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/health/ServiceDiscoveryController.java b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/health/ServiceDiscoveryController.java new file mode 100644 index 0000000000..20deba993f --- /dev/null +++ b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/health/ServiceDiscoveryController.java @@ -0,0 +1,22 @@ +package com.baeldung.spring.cloud.consul.health; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class ServiceDiscoveryController { + + @GetMapping("/ping") + public String ping() { + return "pong"; + } + + @GetMapping("/my-health-check") + public ResponseEntity myCustomCheck() { + String message = "Testing my healh check function"; + return new ResponseEntity<>(message, HttpStatus.FORBIDDEN); + } + +} diff --git a/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/properties/DistributedPropertiesApplication.java b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/properties/DistributedPropertiesApplication.java index 919bf08921..c1d2b0acc5 100644 --- a/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/properties/DistributedPropertiesApplication.java +++ b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/properties/DistributedPropertiesApplication.java @@ -1,34 +1,16 @@ package com.baeldung.spring.cloud.consul.properties; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication @RestController public class DistributedPropertiesApplication { - @Value("${my.prop}") - String value; - - @Autowired - private MyProperties properties; - - @RequestMapping("/getConfigFromValue") - public String getConfigFromValue() { - return value; - } - - @RequestMapping("/getConfigFromProperty") - public String getConfigFromProperty() { - return properties.getProp(); - } - public static void main(String[] args) { new SpringApplicationBuilder(DistributedPropertiesApplication.class).web(true) .run(args); } + } diff --git a/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/properties/DistributedPropertiesController.java b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/properties/DistributedPropertiesController.java new file mode 100644 index 0000000000..da2d37eb76 --- /dev/null +++ b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/properties/DistributedPropertiesController.java @@ -0,0 +1,27 @@ +package com.baeldung.spring.cloud.consul.properties; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class DistributedPropertiesController { + + @Value("${my.prop}") + String value; + + @Autowired + private MyProperties properties; + + @GetMapping("/getConfigFromValue") + public String getConfigFromValue() { + return value; + } + + @GetMapping("/getConfigFromProperty") + public String getConfigFromProperty() { + return properties.getProp(); + } + +} diff --git a/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/properties/MyProperties.java b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/properties/MyProperties.java index 7accac168d..d92b18ed51 100644 --- a/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/properties/MyProperties.java +++ b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/properties/MyProperties.java @@ -8,6 +8,7 @@ import org.springframework.context.annotation.Configuration; @Configuration @ConfigurationProperties("my") public class MyProperties { + private String prop; public String getProp() { @@ -17,4 +18,5 @@ public class MyProperties { public void setProp(String prop) { this.prop = prop; } + } diff --git a/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/ribbon/RibbonClientApplication.java b/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/ribbon/RibbonClientApplication.java deleted file mode 100644 index b2b54fbe8e..0000000000 --- a/spring-cloud/spring-cloud-consul/src/main/java/com/baeldung/spring/cloud/consul/ribbon/RibbonClientApplication.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.baeldung.spring.cloud.consul.ribbon; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.cloud.client.loadbalancer.LoadBalanced; -import org.springframework.context.annotation.Bean; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.client.RestTemplate; - -@SpringBootApplication -@RestController -public class RibbonClientApplication { - - @LoadBalanced - @Bean - RestTemplate getRestTemplate() { - return new RestTemplate(); - } - - @Autowired - RestTemplate restTemplate; - - @RequestMapping("/ribbonClient") - public String home() { - return restTemplate.getForObject("http://myApp/ping", String.class); - } - - @GetMapping("/ping") - public String ping() { - return "pong"; - } - - @RequestMapping("/my-health-check") - public ResponseEntity myCustomCheck() { - return new ResponseEntity<>(HttpStatus.OK); - } - - public static void main(String[] args) { - new SpringApplicationBuilder(RibbonClientApplication.class).web(true) - .run(args); - } -} From d562f429a1b0caba463e90e83ee8f9d3e82e1a21 Mon Sep 17 00:00:00 2001 From: tamasradu Date: Mon, 4 Dec 2017 18:34:13 +0200 Subject: [PATCH 50/50] Radutamas/bael 1265 update junit with lambdas (#3196) * Code for test article: Different Types of Bean Injection in Spring * Adding jUnits for test article: Different Types of Bean Injection in Spring * BAEL-1265: Adding jUnit for article * BAEL-1265: Closing ExecutorService in jUnit * BAEL-1265: Using lambdas instead of anonymous classes --- .../WaitingForThreadsToFinishTest.java | 28 +++++++------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/core-java-concurrency/src/test/java/com/baeldung/concurrent/executorservice/WaitingForThreadsToFinishTest.java b/core-java-concurrency/src/test/java/com/baeldung/concurrent/executorservice/WaitingForThreadsToFinishTest.java index 17b71aa35b..7e2bf590fd 100644 --- a/core-java-concurrency/src/test/java/com/baeldung/concurrent/executorservice/WaitingForThreadsToFinishTest.java +++ b/core-java-concurrency/src/test/java/com/baeldung/concurrent/executorservice/WaitingForThreadsToFinishTest.java @@ -9,7 +9,6 @@ import java.util.List; import java.util.concurrent.*; import static junit.framework.TestCase.assertTrue; -import static org.junit.Assert.fail; public class WaitingForThreadsToFinishTest { @@ -40,16 +39,13 @@ public class WaitingForThreadsToFinishTest { CountDownLatch latch = new CountDownLatch(2); for (int i = 0; i < 2; i++) { - WORKER_THREAD_POOL.submit(new Runnable() { - @Override - public void run() { - try { - Thread.sleep(1000); - latch.countDown(); - } catch (InterruptedException e) { - e.printStackTrace(); - Thread.currentThread().interrupt(); - } + WORKER_THREAD_POOL.submit(() -> { + try { + Thread.sleep(1000); + latch.countDown(); + } catch (InterruptedException e) { + e.printStackTrace(); + Thread.currentThread().interrupt(); } }); } @@ -83,13 +79,9 @@ public class WaitingForThreadsToFinishTest { awaitTerminationAfterShutdown(WORKER_THREAD_POOL); try { - WORKER_THREAD_POOL.submit(new Callable() { - @Override - public String call() throws Exception { - fail("This thread should have been rejected !"); - Thread.sleep(1000000); - return null; - } + WORKER_THREAD_POOL.submit((Callable) () -> { + Thread.sleep(1000000); + return null; }); } catch (RejectedExecutionException ex) { //