From 65d6e0127fa31ae92f0aaa25a96ee9b6c4f2972e Mon Sep 17 00:00:00 2001 From: Graham Cox Date: Tue, 24 Oct 2017 12:51:14 +0100 Subject: [PATCH 01/17] 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 449c107ef224dad82f7814c0965b69f632c7b894 Mon Sep 17 00:00:00 2001 From: "Hany.Ahmed" Date: Mon, 20 Nov 2017 21:46:29 +0200 Subject: [PATCH 02/17] 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 d26adf690b615d5194b5f1650b092c68139445c3 Mon Sep 17 00:00:00 2001 From: Markus Gulden Date: Wed, 29 Nov 2017 23:50:00 +0100 Subject: [PATCH 03/17] 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 f00c95f4fc2973ddc0e3cc7b62a259307bc0156d Mon Sep 17 00:00:00 2001 From: Graham Cox Date: Fri, 1 Dec 2017 14:05:24 +0000 Subject: [PATCH 04/17] 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 39fb51e63d1471dde1d89b0c0ecb045545b17a6f Mon Sep 17 00:00:00 2001 From: daoire Date: Fri, 1 Dec 2017 16:06:12 +0000 Subject: [PATCH 05/17] 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 06/17] 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 07/17] 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 08/17] 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 09/17] 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 10/17] 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 11/17] 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 12/17] 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 13/17] 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 14/17] 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 15/17] 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 16/17] 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 17/17] [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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�qQ{f9-f_*QTdSD9CM=LE-E`3A7;9{ZD#J10TO~vk z_9gYMafLD8J=WiRc3pilo$0F}iItDJR?V#Ia|Gix$B}<9Gc)PlN@2s_gCde@TV7pn z4)($K3^ca89dd9^aIhkzju2l3LYHgui}xY1so;AUzL zAcmm@i>^t)FTN5Y+>>E0ndM_)PP>p_<2#zl_;juWG!}fUkL2E~9p6h$MAi?((m54|JjNw2t`qj|iEBwS)ZEKO4}+oy zxXcr11le2T)kHNof}e)d4~P59I_liw7f*`4*7Nu}q0yQX3B+}~eDTzTaR3*7xovjz ztbL3sn)i4+VOYiAY86;i3P%*^D(`ntYE z1libzNf2)kkwGpn;aA%2ka_w{C;d0f zCiJzjUvL>Ela<2c!{=inhttZC)dfg8ZAfFOsxDxny80K+?Bho|?>%G9WtFSV#4vVE!trDeQda>~fJ*2UzJk}BMn@|% zLKFk56j8X($19AU-K3eG%{M$aBZH-VE;;@S{gm*%HhJcb)Q>L-&@(=NniCZY>u^|v z4%&E*$BEhBH%(9ZGxk4iY1M7IPV$b#x0HAE(p8^tA1 zoB-pnRU*Mg0<%jRB^yVCwn<(GS;a~R69Y#hWRMn+WcO{{ZBWw&QG z291zTCdN@^KE8eti#XHt*_(SJF7hWeA>fMjT`uPOH5rqGs4SR|@YHnz^(#6A$`zLY z{I!uE=gK#Q-`$#lL_dx?FtB z1zVP-RMZobOqEG>BU^?@{!~3<9|j5q21Azt0d{v*i)Z|~&22L(2N3g|%_;FQaQH6~ zla*t%0B7FSuy7x2JJn{KR3cYj$ZaK)u2|PP^ue7JylLSLa0aS?8PlPy3b`Z=WuIy( zHtxMk{Da>(ji-*Jq!Z>vKR|c-8gjW&L|8VljEBFeJ%%eaUEu@&{ul{9$<>ppFcNY?sb?jrAg8_zV`|yIjD{UfIHU)V&);?&2D!w*@aqr>io8XhtQF{z63!kLe>c zjv9^|V8fNr@}VO4Y{wlgrL;}CW`)TP`C^(oRpg$#?~SrUt@vr zg(I$|Kkc&uNFI7eG_FQ(?=nb5eEHX1`+rP`PAGnUuP<{tB4#xXxy0bD-uCQ>vrpMh zh6)G>&P_Kv^E61&Mm)U=>l-1qtb1bDCX>z%o?5aV;w4g0i06+5m16JIPRnk`rZPQJ zCtabIDpi!m#i=(~sBEjM|D89=;!@)I`-0Q~KDKo8yH5>=7{<(>m4rf--Ej0-rFsH0 zbaneW*|l0r@5SGI8^4II2AcK&{3w9j3DMYH-6lBLmIpeJ-H?bdT^-JjHkB(ya;bNMdsp{yLONL($F%xB8Cb%B$npU3owRR&y zfS5&l*J8Ag#b${nqCdfxi^HG8adyv$=7x9mP2gOv@C#BHB|oO}`D&peaHg9$GZmaB zLX;L6$%0XI{PtT^HCZcW++O>kTznXD8fU8DEFpc$PB==c($-)v%J4MV3zCPoxAL}| z|8`icWk!4Q0Y6Tvt&9l5NTkA(K`KHwEqHeIP4#%D6;`L7PynHh8lgR&E`xHKKc6!G zP*n>CYpO)nVT#a|xe4^Mm~bQv&lEpD?kl6++2adR+?f1l5%EQA?l*jdN4a*K zmZHH}GTdz&(VB6Bd!X};j6A{6qfo8d5h9={@@;u#oq`c>IAW3$!sTH=^k;#h?mLX@5@QM=eKPoy zEaU*K{u#N>FCJhK`Tmf%vO@{BAj{agmp6}^*v+WGfXtC6o9g9&;8VzPaq#MJo{KIi zTXVwMOl3=OB;tM5)x`9I6v@<*`8`9{Pdtf$IHz82*d7`5g|bXP`h0gOC+G!9>|R>E zp-ei@=nriOC<8j z;prfTUVmExZYE@?QvZy=+dv^Xjvt*`tmUkAA`FnmV!nmDs~WV1`=Xhfz(YtG*N;xW zC>As#bKZl9PE%#@TSy?frizUW-B;41Jyz^V*I&{DrX`+}%?fxr` z#|fztfFX(S6>C!m{n8}Qb;J#_tmjjl$q?+c-Lgz`_M`yzfKS|T4nt1NTn%cp$BZYn;an$$w$39w1 z^DGDhNe#xA*grOW2Nsk2z~lvHm&>U7p5CDc;oksK5iyONu|b50%;7eXWQNpRyH&nB zd`3W0Z@fG!#uwu&rUcPLnWi(VAkj{+$B0Rg(<%A`^F3x#MO1%@HxXVVvD+$rfU1f>Z&=BjBaGqW zQ%ReO&fzamrw=9XNV=FbIHxmc)u>CnbWn8Cjz6$dc+9hez4_vv1L%`Ijjo7a+cU~y zoh=t4;MQ*y4Wn3(6zGG-llo3*|FC@4XHucoJ0*g}sBc-kAPqf_94pUjJP49T;-_4{@@eF9eQF&;zvTAE?1x zFz^c!+_ge2w$*Vz8<4Ac5??r^S0N70)-?iu;B&|_&ce{_|G~I+lEYm+kkHGqMooxn z6Zt+`@P|T8&Yi5|kj}B)*?F_QkD>$Y9eak1Kc(>KBH?VZ-~d3#Y8qwH3=>h#GHuxF zeed1u-y7`JeosTEbo*#<;2o{wR9}q!=k=Z_5DoGrMOw;C`n9H1SDLinCFh&xtlEDUP;Cve z=+tQlJ!-_nw9>fDFCkp`*~c&q8e}!x1+{{uY@}qgu=xv4jJf%=HPFLKBr7wLwe%>g zFmm1#7zHvCFf$F!Q&IYK;jL<2BCUV+EEchS6@?v}fRY=RS|#uJS%9{MO9$o{kZ(=; z{DP6`i@L+E09pL|Svq zS3;dF#rYgdrbK? zSx|~L@QuIagkaJNtPaF}ZMouP`_;fAOIJ7~N7VD>1!)euEJ+;pY~4N^YdsrD?XDci z4Hh7&;>53%mCr^-io9-fq8?j#imh?Oc~XbIGyliQ;=^p+B| zb}DRoE*xawN&GYnr+NekE9@caU$gXg(4`4yn?gUw(4YtsWfzH1y(>%6W2>f7;LPNaPymFeyY8`#7X_Qlo6sC8Jc76sn3zBaNQ>>hoHb_5`7pB6 zfq-ayoE|%jvV4QKnI#hv==s2@^kKLNxIoD?C&YP`l;YMv2oT7V%dAcfASL4!b2Hdh z83!fk?p@qZQuio9lYcBaFfNv31W^}+--3j1qbGjwFoP+MYwn6JVtj z(H=%iMR;y$|DA5&61gbBI(85#tRCkbJZXF+S-CiFh?(ovUjh>nA4&*v%U_uhPR+JG z-!;2j!Z;z~f^TUsWa)_rSyC>6NLA#yK!)TYESlRT$z+>MM?TP0p_P^>Y^q&K^*#!D zgB|iSAFxxnQubl|oehgp!@Cu;?cI?9C$3t;8a~sW&NraN?OtrRkRN;b0u|U!@d^gx zx>YtRVRssbk11DdJ%Zs&XkQ=Ew=*sY6KTT3%4gN;G&>19ZIPR1%}5DRhNuvJQ6#JnJZr06^gRAG(i zO%31)HGt?Fx)s7}8^JIG>~ex#{{)B3st3Y_JqdKj%YEwQac7N_EIK|0KNve2Bj19f zu4d&uCNU+M{rElH6zo=ZWHkY{Mt3&g10ej?rkk<-%nB?%cI@h(DZZZRS=^bDoK()9 zpGHh}=P`OjZ-N(1dg2>8NPgyU%I|$w@JXOwT&`AoNL5H|{ba!HX~2|0=s=8!1PhAi z;IkNb>eJs+E}Y7`W@?)zP~azksw@8 zLnHvurM(6A+o!^?Ax$n5T`Nj)diG%-o%@+uy^bMrZRaAdjQ11NXy4%Bvoi1mYmTSL zr6Mofpy@OUi?0tz?JkhCZ6lH!A}h1Z7}Ga zJtU*o_G;)I(XbOkhhZxn79QZlr~i>9KmRG}si7Wso&E$lTCRa)Qi!(1HlJb)aZr=7> zNKNQ5gX%g_ViW&%Fl0dJdtIn3|B13}QQq`o`c#KnqttwWzVx1ijsI90HA5ztjp#&d zZ*(S-ku|nfrhJJ?^YdLz(o;-E*&)QCkU(;K<_G;~2sn-zXFWjs%Et4#Vp{<>0k*(+ zN>U`@n^QQwq|hy|CFH>Q+vJRC`O_-K{7CGDRApw=IUw^em(wderAuye-(QvY$|>${ zod5H(5^w^dsFcXIDaXpFV@hq9t`AX%)-3lkW!rTA9m`?(r4o_Gx(#$9SVYIj`)Z=? z09RUUrQX!baxa`tgS{gtMkf~-jgANOfbtO=Rhy5`(`0sQYmy@#P$s$H(hruBJn=ZV zSDzme>Igb^*q5~^9z2+88p+!+;5$zVB;RBSq7H4-j=Ew&*74dmWX`30j+0wEOc4=y z7fin|$%<^a5qi(bL#KkG7mE9F{wEBs3>_Db`03h(@`Vt(8)2en^Jg@~Ll!CR!ik)5 z5@|2=SAD7IAqVtEpz#D!j{iM1eTWI6Lv}*pB%YXRlZE;9_Qohv$ox>JgD8(LP)lLg zY&MOjxuLe=f-%SMD_*)gk#Hv(s?7B>3S+y*F(sKF9HPvcbk5l&NQ9fe{ad+`nKd3q z4v(s~QcZC@qG#7g`*7`VB>TWz%K^cIQEnVG>yYp7PC2E6-efA58(4cn%yps&7%eb$ z7~YEY#Iu#BMi>*$wS;QaxS;gD1NLrYFD`m9y~SGQ_o{jm_Z2?ebDW@Pw* zSP`%350uvVc#_&u&pDqkYFU{#laW&c_J7Vf@YfLm4330Bjgm5Qc;MUzqK!+YQ`MdS z!`53x#lZyKqPWZ8P8i(XgEP3hJA(&zcLuk?-Q6WvaF^ij8bW|zK>{H+-+#_M>)f?& zS3lJ1wI8ahyLZ=9?eoM}=t7$YfBBlM>d9XimndN@P$2WEEsCS&@pDS6f?`I-BD|Bm zQB?9N=;y@F2`)za!yLhRjL3B2BHZpqgjTD-*eT)sB*jq{YbEWSn`ld(+6+NOa&~EX zi&Ilhb1d40YxMMiM1h_HxjnlLh%d0@9CQ&*NAHUNiIr9g&6%#=+P*Cu!QhTsCiWR{ zY06MFlmVHQ+p__up%GB1X#XAkj23XU4 z=!(`thMKKB;4B@!IGcFGyu`LsuK4~$l2+dc+% zsEIngtzh)p2HC>X4Nr{n$^2|6TO2BneVtkxw^*=toXl?<`zoeo_>tPI2n(S6sY3aS zEB9$%iUazc{IYakm=owTa?kww(WU(p*>zA4s*33}_hU#nerq)yhA7l9fNYofj?V5; z;7@9|xk9r?-j-CvwM_QQ91r2~+K3n76{F@TT^nYV)<$`CnP+%-W&-bW^C|DEY$X%3 z3W#%tmaG0(AU`Y^o@GEHfZV|~eC-b#w$v3jD8~jjx>!!2Sb}cms;V^Gg|%9NEbU>E$%9(a@KZI}N^?sC|!)p*-verHrBn)VcfhkNPr&pi)l9{xd! z3xU7e2qR&wb6C2R*$fs0JHciqw4fxPj==}e8*zIpE2DLn6lL&Mon3c|3Q{})cirEK z3&h+Q99$pHK6D?vtHCLBNsBoNkP!VSlh*w16uza?3t30ym9=d_k8zfbyn_=u6knu9 zQgVNf9;pE4+5af6cQUASr0p~!BJA^%I~Dv+JV6J`U+mL8w;bUXB9=sy#Uj?v(efnH z@)SIsum4KZ0{9CP=JJB-DVp(KZ#S?YCoUM`iX1X)6~aQgTR7_taU4Rf=^wF)X^FX` zlFjqk7>pk{S9bntEu>=k8RbEZCU}s)t2_#^L3u@-#$f*4`uq90G+-Xl?>rK|t3;p; z|5Cw8jfPpA^Boyug0)ePwo$WDg;tXk+Lt8YV7?&+(Fp9}srljbkW|3rK&c|qQZnO$ z{DLYDgkAI}!jbT7xkqL-< zu%!Don0V%E_sN7(At=T{O6B@eC^)jSMIjSH0gs9Lv31M5j6E1JwFh3#Hz?8s63$=B z%B;R&TP)=SR)kcv90$P}r4yJd@SlXi<&t!NAgoaw{~3$trGvlaGUduwk_Q&4XZ!U* z7rNjb!d!VDZPXf;IBEl~X{d!jJ^b2ua+7=}D^iu=bg3 zebrI#-1r&%)mfStZYF-&G&vV~elE1Zx9V5}wx|`_Ers;KITrhtB_!13We2_^1d82N)R_C#knc`n8nxXB*XZ%0pmVM^RXOFBmtEsQ^CA)N&3 zZNJ8T0plsVid({~yVCNCsZZvZq~`pNbyx4AZ;`jICnlFmBNc23#xS0@z(n>dCHrB+ zPqD>hf3VrL-Gd|DC4x!&jYSUL=Yf@R-;)(Gy-+2ugiX!IEo9Gh3Ow`m$tEAt9Q+V`9=s5I6ucLF7CajKXJIasU-W2^Pt0@RT8?2q?d4HUCtSfrv z3HMLVi_%B4wzd)+igNyx&GYDa5WhdLZDHBAv~F+3kPpsVwCwTz35W3P8#|tZf(5^n zb^79iTslPw?n3r-XE5@IXN)zbrmnRT7WY*0ZVCtT@xn25jVrtK>3LiSGpl@y-b+3f z?bys(q!Jnhc~vJ%dll4&@MK}_LC;QA7ls{k4xdU}@V zAUUr}GkEAjv)Q8ULx{9_Y!$v+e#mHl?c>+|8^^yeTNl`e;k6g!1qd2JgV_x42!CM) z{Z(IM(KKt_QkTO}ZpNQ&tu9m|fDFfUG&lbRipqCl7%(?d>k~)6vkzm+g?p72N0;1*bo63QPd$OsPR}DV zm89SlRnDYDKR?szW z_Wx%oJJBaTz;4V*z&}Uef1GCngpa8`d;)Y#g;WaSplcCqI6_EeeY_P^*`^!je#(Co~|h)O?Op(lQx!X&|#{8Z6eG@ z{rD?S3#G{e6A#^Yt24-s64I7d1Ui`z4xZ9rJ9`=`4z71SKK)AC7_<6gxIFhx2Z?-4 zqnzqdzO@V{mK$w~vlWue*ppy_cactu{a_z|Apr8oy??Ohb zU(-Y={!TRe?HrM{qV*evJ8y^xI1OOZQu$2Ke~AF85~MQF8ASb|dWmRdq3 zXd#~3QP!o}4l+Vt;j_rQ6!?4x_nyGRTb3`0wYsRU-f}}f!Ge`M9$RXu2)ufOg62-7|IC!qide!eY?bS#$O6!9{`g1X@|$uRPjn zaDpl6iwRsv&6$<0xgOvFPfto_r#~-(1;uMoinLGUE4<8MPWPIZe^PqSBEKFdg0^ay z8nYZNs<#hCe)|hyf-z!e07|!MhADqxti@36R`NmRKlg|YxK9$>#3tpYz^VK#ik@igX@c;cmx1SDJ>>C8!7x({ITaN*Wp3Xva{04 z=N4kX43CK%N=1-;%3N+jHzOOW6crn_r@|+d!d6#1sq|DbjkSqgoX{}HlZSdjcRO%bptCl7pJzog}VS)x`dlGw#3?vS*UL@X>k+Kc+Rf}%w3D<{n8JdXN4pK6xgRB@-3#WJ| zmPttyG!?r=>rZfoImU zoY7zXm-!Z!N$Cu{F0J7!%jCc_UaI|qRV#{w6N?`@M*S&1&*$Ni&$2*4~pyddy(+}F;&t%s_NeI6lH~PyG|KaGGN+&B& zCVJ#Q)?k(7WS9zdN4cJxq77iQWK~10A#BR#iOX0-TZx5arNx_;ImU#;N%vdNOuBN5 zY+WlfaG!+EYPTx6v1awsOSx<^J2g%eJ`FCsixJWxEX#~=PiC-kQi`_jm=|`w`91pmOOPTh#VcHskKFy1y_u9sf!J zyo!Fnx>$D81D?j17Ir7;>E|9)!V;JEV!I+iNgq=oG8z?+X3qjxwl4VzyaL3#Z3*Zo z(+A*Rm=^IMaVzlvN*N8TQ}y03(2ZQ5ldXWQYxow!XF6v8$jnccx~OTcU8$|ROyXyUCMtDNO67;a2ZUj#-r5<7NRqYg;lNu@dAC6OCg{ovMzg`(A( zM(kMfZXnulL9eJOWF8!7^2Dg|+H8$eU7WFnBHR7APW4O?bR6;Sq#o_jc|sjDyxbA1f7M8^$&uBj_(~ ztW~4|)GK~tyyoV8#|SwH3J*wbK75a9#@z}W&AL=N+_=HgZ=&Ax`V(zNJ0yx(Mq^{h z#OM*n#Rq2wr>f>l&Z|XylG-g>dosmSIVNRDR=p!U+jtV&Bf_mElXxGkB0rAFCBRi& zBXK>UHX}QlgO-titLtkz#uEgn-U7vC@J!}+Y$6IoJx%CirBtSg%i9M30Dq7Cs`@d%T2yxKvR>45?y_3cB(8Ux zhX9NDo9CD9`TRmrmAFK3^B)G|YlSIh#_XwnWW!R!jnvct>=v_xhIgx4wjx=7dTyac z_;igiuEdqbqwtv2ZLTd@aFE>lWZF;KWbP9e-pLXrg~S{ssQqL)RBzG>ijj}HhM<5< zGS@K9Ad&wd3)$P3l>QA!w@VV3(nY|FY!$b_EL9{p?#f&{{Y84#<|-Y32O%ysrRkiD zMubn2Dn;Zl+##Gk-vvD|7#ZA4$u;Vu~fRLkgK3Yf`mv=b_^RJKoFqMmk!`&Cx>Fb)}MpgI*@n>54oy;^kZ)@p@p>wq~KfKelBJEea z*@wn75r?UuK(8P|t|dXB6R&S@5_(w+;+d9YMa$rgyT;;)22<9uR$zh+L#C;nN9J;1 z!uBn%Q1bWK@%W9a&y;dn8V4C^THUTrHAK`~&(f}@r~J{sG4fpC$1-t*oL@+q0}{8> zsaf22V$(p-L@x$KQYD{OQo*wLDXX9vGDX%jyjk8zIZlX~rh7)l$(pWfD=J|EMFcaS zlJnFAnmc{fkFM`Am<_0GO6j`erD(f78lE}~@@fZRUK+S4)N)o~Du|?M?X-oFQ>s`B zl+#8jAEd{bV>}isKLYrdxa7@Zu(?9(;{1nc+Sw zD->oFk@>V6D%`V6Uw{Zr^~=gP5I3xoHu>T=^Z?a4k7%nFWW;ncuHA8OKf5AT_OV*V z71^3X>UJo>Utx!w{@1C8 zuE)2<2hqG^hS3S&wtUM71a;qZm!W< zavLGvzKj^7RJ$F+QMeBuJ@0eetZ;ww9vDH;&|uKa?V&kljVX=bV9b9<>W4*CT|S9z zRy|-tR!Q1l;2|bq{rX6h)^(ao+VwNRqIb5rGE?(or;L@Xzxr;sXC85(Am4DE`<&iq z**5Q?sr&)D^YB;N=7^HC9Usq~bIxZSxW@K*xDFdKukV!k>l?89@hzh841X}DJD-`=JY@gKRt&Da#(w;zD5Dnm^(9>$?)akBjbS4YE5wo8i}&VO z0PV`O7{IObz-tN$fN#B@ade%5zbWWHbI0I%=`yh_Y|)q;5NXSZ}QLxg{` z>7g($F0-yvfj)QL7W|DtJtPgd@%9%+RKcf)WJcy2WaX6+@y?MVT;6+n*X;$w`5tpTAD3e&L91D)$Pl>vgjrV$a9whfu;cMFyo^ zRK#6qW+ViygE#C!(g=S(-%8Kyc+Ai9-|v?c*LC+YwvB1}y#?AyG-MWL3VloZZjt-( zAeZ!KEcW~f_Nwag&;J7cZ#LJxA|KM~GchK|)x_*-8oQ%8$xAlhv|EqvJCdy{{mk!ufpwK z;{-9PP)$u25AQiMM%4Q6F|yeFdHa^a_C8?=TO7@yPj@T+%zVfkG-OD(Fm zsP%{N&bti3hSd)>#aPODxjr|wEEi8=ipMp_*^%bl-pR~XRFvbarJT~=R_&Wp5W7eR zxpIhvBln|F6?z;1VSh@7Gf>1njr{p}ghCYJcJn3bDL8hW20(VDD>|r3=roZ zTmO&7!2hK(002^PaY|ZfNVwhnA2kdB#~I@$XeN4^AyPB>%OQP88DNjGsP$Za>e8W_ z-?)ic6H*1{RZ}7xLqRs(MBF1JUmFOot%X5cD8;W}%uXKC?;i!m`c~0W)gTDRO2yrz zyizr9yZ6&)@3GxC4I zuK+AWPTHf}S;tp?FObNgQ8ugpsvgfIlj>&#=R3q0(l^1KI z#RA(QO5MAcETMv#$p+gY1tbk>^POhHO&%!fCE_i`*=$X)T3@sfUtGeS_U!79dGXXj(;2la>*vT{k(6|1u_THQPrr97pJ7;a3B(AB%4WjZ&YI9cuxg9 z#0d2pwnAk9aWVVPB;|zm`q|kz&Vb&2MCjHQP6osu-t#;NX1r+-F;{~>@+s*r4Cc4R z9H<62sQi?NpHU}TTu&8Sxngz=*Ga@iB)6I z{m<%=GndLPR!PZa1E`<6_`Ch3wG+i64IB9NnQuLbh!XhyNn} zF!PC+5O-A&dP}0_)#4FS@|_y%Ni8aFUOIbA?k9Wm*v&j$7J5m9-F~|9cP&8D4SvhV=B_heGXv1X}o9YVt1>{sNbOVdx$p1hn^yU(Wy# z#pdQs1Er%#AZkYaFv{Zg8wGI%lOnxl7F)n*7wCTvDQX(oL{eTm7ZzjhxUc)%6LPd& zLl)oLs+cviMo^*cPPDRo^;(~wh0$1tY%BoI%v!d}KL)$m8NV;m&PA89e+-EzLm$jh z6pQwH7Z}D}$hAtzrT%Xb3Ph12oD18x^axkYV?E=xaoTLNnVg)QX55HEClk66L!Ny3 zKywg?oqES{^Upg8OipR-Y~ey-2T0K{3`I8;DVG8wPZf=&@BooSY2T!PoQZivF;*Uf zxreP52pYV!&5(-s5MO8iy8UJD`)=Hd$O2 zZhe=Az114oaOFY2J4`4HX@m4IwI(Vi7yvzp&A%gH8=q{B1Oiq;bU2^LN@ROJ$Z{bV zJQ!>6zStNj94o~ZO8+L+*Rd<9q%Y!Wrx!rDi#`}BX)5^>)foL;G_TGLaKAUp;TTt>bR)2Jl3S14y6+^uiol(bpcJ@AV{kot%?Z+gwdeXaDX z0x6k~8wn%SF1fX-`r|dNx}zHr=j-mPN)XAwV5g!k$uy@fyO6=z;kiwg-dIhRrzUF9o{>tws*KT1MjMF2RCA#$>po|Zk}Wz8kP=oA#B zkNkZ9sI3XD7#$n7RsNJ>^?Q_*=A6LcYm2uQvXdjmLDKlbrc#7mGME5d;`2=g^~M#^O<(?mUx-e+Df6GtCo4OL+OqN zB+UnrO!)-;q)4+Ch{u?`DC|{W6&&**O0I3@^iwq@@Yd58pI^*2W@DQwXNb;m?pysw zS><*qOiWZfRP>xGnaJP|+2637a!dDBiy#AXEoE!o+?c3b{d35(bjPlr+;1^8KMoPr zsU!~R4Yur&(vuM{a?fSTYIuw%50Av0SE**6yDXYVO-Fr+?ew29qMbfwoFK_QUwJ0{ zI72)UpUJC%@IE$ll*`O(=Rp}sJuX(jCXVnPwa5m3!o;D>&RQfH7u}fanoGGVxQLs= z(cM^bX!_HFsY`p1klnIpuy}e{ib>pDSJv{ZPPRi3Lmv`JZ8brc{ic+`S_VUP{{ ze0?h@KVmg!xH?7dTiW)7$hjA7eDi#d3Lbm?2z`&CoWIoV;aKAxj;!X_W@vf0N=`kc zuz2S7?bJY65Ib%tDtFkhZ(kx01c^~MN`lJqO{C(hpUj`SL~pVE=Yi12%LNn=iNoyr6*XQRbu-htP>!1 zqSpxrk=EAMK$OYq(WYqN3|K5qiqNXG}+%?0pJ| z^W%_72E1a#F+B;Av-gOAnh+PLS&62^mZ<1|5Wy}`f*~neHouirTyhPLY|ej#dVys; z9AziNbezM~b>)@5tqz(P`6mU*MGppp7nD#k+?e9vQSk*$c?_)UU8P?09RRU_1nMA;RJXvQ=Ix6uhns zlFri^R|(BJ&uOQ);E-~t6PZ(dne+Jqfb zUAW!=Q^slhlDf?QOs57b?01%cv8x-(BI8K7&H)Lk0=l5___n!Qm1f2#zdj{+mwwt} z3<7rA)x|ZFvQ92-&v*cDWoc$YSY2j{R1#MbLb1*v3c&OfUKAmBoyE#c>w7_ zBx!tt3uRH%>}SWIUS~%8vcXyouB0iHU76X^=zDB=j?q{X{)Or9!VjpZy}d$2;9I}t zZpD^5jijHhJ719x6~^etKGI!<9(!HD!i2S%aW9RdM^T{K>0cP*qWbGfsjua=#b?@_ zvl=W~*1#~o)@8>8aB(TGP?h2#SUUcNQTF`~*b&_$-k7b4T?78=?zTQ=x=^|jyK$}; zJ!r=0;v->%cz!KK&pw58%HOOQWr9pU;nw`fe&$w|1wDfkT-RlVEr?V~ zXC?>G$s4E;wF!QH*gXr&4U~U_0&-OCn&%yjh?iig2`_*}w}9o9qatzkJr0I)1VhW5 z{d~ukn&SJPX{o4g<={iYu|45+580kdA36U*+0Uj z=@+Di?m9uvk13ivK-lF70nM?T$-;@bJ1HH-o`R#kq#2uSNQKa_^hZ5tp<&T8sWd!_ z8SZhkDRR=tFm&^DWsgO6V(Vj#hvVxodzSZQjseVMw}oj*RLckMjfI-YKck*Rc5<^F zHaAg4e4~UK9eo7@JK)mrx~f?fq$sc8idzNGg@7-X1juTAb(9Mx&1d3pU7;vL8rF^V z6%I)9_3fCDWs2{0SV;r3Cl;ZEZFp(GGg|PB#U=urMXOr`=8Or21U@d(L~JJIeOw=i z1h+CZ+YM_op`Ad{STauQ2u88@!b*w|K+X+VyMP@i&G=Xxevtw1x5$$Nz3BAM-^+i> zfls2ztrd96)-6nYh}=DPf2-G#Xco?HKKykBubmk_i!^zO|rh5(@v!5?TKi zRuYj5bj$rT!$HQ6+PIHy)~yW#T+X4CFHxr|D^?mn0I5S{@)1)O?M!gFJIAX0+iZ<^)vE^6` zke6xiD3Xv4?CGBoW}eb>pMS}tbwVn{-r~;0;31eQ+cC#yg<-k`;nR>5`)h!LH73=2 z*Me^xerb#S$X;7q)@XFYt#r!|94Qurk**gyjIW*b&ouoqO=OV40x@!38LV|!e{Hlo7j)XZ0by%vBSx{F(OL*iIG)8np$IOv1& zXA?Mxv${#59LcMc9z&_nWsox&>PY+YzepWdHy@vUX1!9~NFb2atRMbCgx`6DDwDla zdhbQ-26Z?PlSgIdQ!?%Z9xxp39;fP0AxU$za;KDgL17F(}z0pJ=^S8hC7l_xIn#?tZih@#S8>?4tu~T2c>m&WHqy4-t4iZ zrUO)oga_1WqSUXEhph!!K(h7!*@oS3yM_nPXXxspNae>pr05Y{}eQ%F0(V>rF){}~#ttZi^r z0=rBbnZxc{Q*C}1bG_e~c#9uJr zh1|+{9av617KJ~zgfCQCzvJq7;}x0XbWt=;yXYb@c;VMh5y=xrBwG@W{s52`A364L zBtM&7p-iz64&avO>V*a`ev}5+?S^mxYQ8%DY1YkZ^9|a0!|9-6+&%@vS4oB>C`mDP zYNn(#I>ZLBI2kS}ZBZ77fx5}}_Yh0RLr0&2K|;z$AG;27xyzP}(N!KZYY&-7a$HAM zLd!x+!+6ZNY`s4fC7-G@@(Uq2sm>xG^p8`P52Fhv(T=LgiI9AO*_e6d;Y4p#$T3Yy z@HG{#)^LogQ97T{{34s9=8@q7uxSopw#o1{|KhVE5p@+iI{lic8u>hXIxsCxuHV%% z@DNg8a6V!+REi@5o8Uk#AfvKlJGVO1IB^8yE?q1`V(Ejg*R9#P0p{SU4GQ}Uqw$?6 z#}?%tPG8&Z5bAIhXRPxS#|X4+wQBEdN=nh^k$SfQYiXv3Yj!Z-a3_Re!-}Lt!O)b( z-ID7B15aMf1)ph*^`AJ3IqEA8C>Mf0I>Y-+WCqMidcP^d68z$HLnLGDt4BPS^mvqL z)YAVv%Kht3@$_aC4}L}Ij1w&8fE6Xqoez(0A|<5~&_*Qf<$q4&a}5`u*d zn)Ud!#Rpej+9Fq{_=MaQKHZPWHO88x5mn~d2O|9Z1=T_?uqGsXF~bX$i=yCwcut*r zQ0#7;r#`d)Za6~GPE@XkBYN~Ja=h6nOtZ}${1FADPDOf%!jAH@lc)JQlbbG{f2C3F zF)>|nhk>>LqYH36DOuEfKiI;#p02)?%PuP@nT9v=WS?`^eiT2fYm0wd`z*Uv^g6q? z_ia3te@LV(DE2Ay0tPMPS5_DncO8CmS-@CWU88y2hSYO z(gt7mEb-7!T!hfSl)2^WKpt>D)Em<`t0~&jL$Sd>djo23yi`9xwj26@r)dPWlv?mo8fHhAI9C)}-XoCGOf~8HNQbdl*d5{X zjTlM@`(&Zbq5+_ytV~RFgPcpuO`okWKe3wRI8N*loHW`c6t@@~H?P*Z55!u9@i^~$ z;a4mkvsNFN@w5~P@YPoLPQOj-vH8SEyw2(xOV+O+eRBAnX;{QM5_WA^jKjLjV(`JO zHt(p^LzUX<_n^l!NU|YqhHYmI%41#3W1C~F9*!k9ta-35qrT85=-xf}2a%!2u&llu@AM3L~~1zmG_(o8Xp)_EzqJ!KA=`VLbyW z=8GJ8e0VRqQ{Ng1nguqgg?>RYHE#pwN^&y~zfw1|e<|}F4yHPlj4xr*;om*t1GPTY zC2e)fbBB#-OCZ1{HE~?PA6l5^3@sdlRLjT`rmiTnbRB$bLseiy zfvpiR`GrkX&2s4i-&0oK_(?i^s`sotAZb>(*WKwONkV%3<4jecOpR4oyryY3A>ttZ zK34(7p|Np|E@gRHHAvrU7GzY0e<$g#bO%giGNu2NjF;iHC(1Y4p`r9~(G)i*=-eUL7hal$AZ+JIt)V?V^U^g2>Wr;JvB zA$0}gSH5m<@OcWVUVF-6N$6=6WM;<`=vVls8*2I&27tMZd0XvS<(vQ`mFXsK6Dhwwc49 z3+nTy`VV%*@J4`_sLsVajaxLd^Nc^$D^2n{lEh()`ETQx&*BiBj6+qITbztS9jiAP z6@J1caZfrtuI4ux4|E%4DL?f^5Vkp09gNPF@9Q*hX!0P?1AjwVJ@_9_ZLG`qa3ZkR z4h9X{cv^+Ofrb~pY5hVxf?kx|l1vSa{e_8*)V(*DQ;N?cl_2p1bmz(8ArREY_r~s8 zFP;xCEssroZ_3tX%1``KA(F+rpVjBsv$x^6Ts@wS;OWklLyQE_tN51>*Qv}WgIz(N zd`?~X7pt2pdhBZo{N9RWYVrzd>h<+A)CGyndnSg!Byd)=`h^N2uBhu3);SUrjty-6 zEFk|Ei0ek?K1N8R7cI|0v6D{E23SE77grJ=C;VbQW6pI3(@;C>sOb#chM)(jqAI9@fdVPJ-d}J1CA{3gk5u$x(q^mPiYSH)}A;BJ}4>PbK3Y|Pw-GtzK*u&yRtok zd07kgF6{Bxs`H+d5PqPC)m5n<4TT(^ihW z#C-lT$f04-gWXpFPiNS6&2laas@c-6!mDwyVJSt%uTr&)PU`RhP^LIDYm(LeJEX4} z-BL5NDh1Udrt=yGy)!>K4HJzeK6lZsvZs``B)mk0ek#tPXbO^SMjz|S!5@lsl$lxh zfjJ_XV%_ZLct&PsNu)XlW^4LZmdw~uy})GcmrF`RWI0ai-4_djuv&;A07o|O>w?d2%> zB{dNz>N?IU^?1#0bev`HvG3!6R{fLs5x$XRsGc$-&k_@QtJ_~w(*-VJ1cD?}g~V}B zB}ULUU9z3N@x|=%*KUuKg>X{Hni85$e%2kYjT=cJv)S`uN_#SRQ;d+%it0Xx6KqCV z>@_Js38cnsJeFu1pYx5B)pd2-2*VUKZ^}-P!1BYoEy42beTLMNJ4Ca#4#giPOjL4| zEqMbEX4^S*LjA?e(Lzsu(k&KkDwzS(eJ z-(~~6uB0P_hS;2rU(U7!d)_OZsHco|DdHp5f0=o zc6GPT`Z1^FisyM;K05^|ew$tOa$Vb8BFO{CS6eWf1eHbd@h#L?DXnJ9aaf|&vr~Ph zuxtqWwyRoPXkDML^k^;CWDGDj!#X=1Q z8%K!XiTP2SlSTR<=WHgM&NDv2tSi13ifGBgTL@-}5B-JeyUj5hD9+tG9r|l!FC|I9 zJ>F(?K>tn!cQ4#Q(LB|NQ*M+w5_HRIo-|zI zfz+{y3${YToJA+}r)wtf+zmdRk$02D!Pob%3O-h!NttE=_Sth-s{74t-g?0CUgW9m z!1|Gl-zdwEAqEacCmv;;40Z>Z*WY&$G(C8%geQ<*WUrKe#0&Ay%-kH4`Y7MKKFY_q zrDN@Qi%dX?GplYCO7g@cacx|WzGL(KgZEMk1e3m~uR|-#?v*);U?Vf1JuaHuj46CmN1H+D{#@hd3j)MIg z-Oiv33j+2#F9~Vts|OI0+copTtZr@oJl_$oyU`ix%2zwFrKa3l(P=?Kf|F~K z3Vn)G3b1WG-{>st@T+Hrn^51pwO3r?IuT-hNV~E=c3zc`2`Vf^A#IcA#FFmehA~iuLxY+L&`;*0T`So~z2=TJM%^lA>@(Bwg9B5{3F>`G z>afIOgYK>t{|`320Q0$V4|4`3+$>cS5pHIHD@VH{8oY6r=%*&6J~+|&1n0O-Qn0QvGg*+7HOHVeIzE;iP`Qd;95p^zO+pryWlA?ZW(i3Vj$=G8Q$@$8MOJi)LVfgxNVdajjx}t-T2a zr<`#`Rqtk&5lE3z2qC!=MImjzbljtq@uW5CT$dzT-?IJWS$UAA43IN0i;Y9?zY!)P zJ71pVD0$5{7HoeGpUr@8T4@q1Y2f+xU2n9pjeu*W#%GHEp1rx5TgC;?p%=~3x8mfW zTxEXJ)Hf=MBC6FdTZ%&}^6Qw0yZ>lA!LrKZEMUthGG35M`Q|k?iPF<78?RDOuglXB z+7iZGI?P^Z0m0-gZqjV^in0rm2`$%wP3At+EqHHf*d`eK{R5ND0KdZDSS83$uRrD< zHmqTDf4qe8YJVfM%&!l*dgG{a;cLB{w=$*&uNzWEt}?+Hue2Q8h2~O2LB^i(fM&a> z{Slcyd$y1IH1>|-{JC}1HcrgPJ0)!c7lsl-Kn4Ed60CmZv)QbIgkA&Dt(LmYfere~ z^Vk+kCX@UdC~}Lw!L0h10Sal)k?AGj@)H?cJIt(s^D6W&OdFjRx<`M`OE@saG+7ss zxWmx>h>@ZZM&P*JkWbfLvtHLTa{7}MF{F0vBS5UkBqa1w0#<&=4=*WI$eArGE{U~$ z`g^R=+6)u33@w2V8y@>uo%7g0c9^hU%l*C?K={j&ASkQ#``Em#Lk?N-6~1&Buc9{d zu?)R_g*O=3UhU|vHma|gu|k7^^#|QG*7vWi4!9{W3RS_wn?)Gc-3JcvfdR?)H{|uXfR7;;c33xM_>y zaaO*?gnN2CLooa_a=)R*)xDo_hfD*~&|+eckI=sM!@f!Cv)k85rJdLRg;9qocFmFQ zknJD08Chw^cn~d7VLwZSCxo%7|0)>`Z(5dSkZT{P7@lYv2b}&{;`M%HJ$smDjP2EQ zWK0-uG_hq}FS;|NtUscoNdj>=GfEgWZmF&=vKv0cOy?#ut9DecSAckfzuEniKxG6;pUAuKISEoEdh9A&O)K-zE!Fl4qah%yKoa!0(CxJ5uTow)?Sd~!eJj;E#& z1UDg0_>~G(PiNtfLRS!1SiJ;rGL}k$=&S%nJ?!XPwxdrx7)tRRT;RJ!-eDy#+gX83 z4SAOg9JX{|TJgcBr5Yae{!rYk1?gCD#R=NrT|`Z_@_~S?sZ0|9ozxvt4{Q2i6s;wY z_v(l_^YQSd%fhFPDE~JEUAtczEpxrg@&Wa$c^v&y(FnQrI@Ls1Z{cVwoads;Y&!xk zaT5kV?hMMJQ!T$ee`*6 zE~x(x05(9$ze+bP6lgj`eN_tKD%o~Gr^G_97 z8vg*3D%gC^(sq!iCS}%O#2m!fF%-zTEK z#B5;fXpf^;zGgpACB;{&_xA-`b#FqU7AI>AKi(n*EYP2#T0*o9?y(7*>Sjf!bwEK` zcGdHVUIi^2FIs^UoDgHOz+mtIZ@+qh*9!_3vEP^|ub@n{F$ZF4J59=ROFVDZ;w_y? zgRL1okr|HDej$|H3BQQ2lCl7Gr!S&AfIva+ep)=k#%c-x*0D!IQmo@OZm+UgC$gm$ zzH-Gt1=%@>+`#Q(>+Z(%VN3$Ge`#M|v-8?3z?2p*Iwj2BD`n$<#Hk2QS$(^g3N}En zDp}fLgArBds2Fca&E91jxU9jbI1oUKk~I!;#OO2jnL|o>sX`ESo5v6}eInguf(R>& z9p+r;)SEr_b*eJ)e@75%0n<0_7;K#0LGc7%hLHCXDi!T+1A1Y*;r?cy1?3*Y{lo`* z7gwhc&*xY}b%%v7?Eq{`tX^hCpn1j0P!Yb_mJp?J5-DuwNQ96WT7fZRS<4Bs50|NW zC@7|L`;Q?f7-~N`Hr{g?R1`eKObDlM>i`1%#~lvPc|p9NB-PWVl-?qOA^1G~?lJCL zqMjzbxttnm^D$+cz(fG`ab6)*uE{NZ#3!Q;1rD?6VEcp`BL;5VEVw8lhI6(LwEIgJ z*P9ghn?MtLLvM}DC#(mj%ftgAN;*`r2}R6Iuqz158KH%70M)TIFz{OUnbjrq ziFiUHXs>BuR##|h1$xZ&Q^F%V9P4nvYa6nq5kPOPLRE>>A)twP%^zqErg*%e=(D>z zh zY`+}GZ@>;b*$|Ysa{SzBTsqlb5H-btJJAwowmA6AVL{bxnU;gQ&3cLuvn^iBi3lxi z&$v#dHhMTc8Y4r+Tc%X0Ugw#fpHC4d#HW@%E(9uT z{15Q=J^eqrmdAUA2Jh8H08b=nh!9shxl!_TWlQunFe(5(;-!H_?{N$Yc+3G}9W>k% zY)+|BtA{CB2^W#FwJc?Yk%q@xG6sIm#Li6HFT^ZibD$^M`+pNaFndIx9W ze=);f<>Tq==zTsu zj2-O2wu;k;frayc$5@wE1F46sGH-_~h^{{Zw)Tf2a+z3}Kuri;L~4RK!Y!$7-GkyG z43j#y8szI!HWvlxsrM3F>Nijq0}kwHgKV|;k8!*&uyHMep1){-h&d%)Izc-S-g8~J zg0#a%E;Xz=TDRh&IWHn8@em!VjmvTY%G(=Ky>|VjcmtEJNkPHG(onOr2tixO>KykX zF?i74b;K7%8O4&Fg2Lk*tmovME>mjo3(VLqZr6V@wIl$ok9@=O^x{~xhOU*l zMqT!zC;}_V^8*I~ceUm=UVmgz6b%Kb(CAJfz0qIV3y1r3kNUUqDF9xdh>1Qk@W(w6 zH}{H_*KEIW_#L;xHamc{waj!CK^t^(oRH96d6{4Z;^mV8y+BEZWb1nG32~ag64!fv z(J);jopQhevjnasK$R*~R+PGsupSZn5i3a$>FQZ5L$oZ=dd>#V%qgfVjN%YV7q|ei zb)cwPpuP6W(PEt@=c3|7Orf}F5i!efp!{a^fV>3)8fE&l+*;Qc?l zl|?assc8bvdc_-~7-*UTn7gmEaB!U*F;FPck_%;2YNt0+=9pnGAamB>7KAru^FlUV zr3q2_oWOyc1^wJ(_hpQ7<*BaFEahn3f%lde3iGrafWIRRbP##0KvBbrxv(#$BhL!8 zh}%STE)*=M_pDk0y74!Xone0Sgk!zSwn~XgrCG7bGlY6s2qSds;t&Ol^H54)u95n& zi4r5Bej-Qw03;wf&~4FmaAzOx{m%F3{$ug|9%5+U57bXO)$Sm*VknWTEYLE~Q~tPCBQuRjE-%>6lQjye~mzxa_Ig40L0OBmTWkQ zaq}%b%T{TP$a~Cg0QJPDWqXO*VbiSH#dgf7*m!Ofq7_~8MGR7YJ;ZGQ9CHE}P-Lpd z@c#hge@mB{e^%W>T)A@P%O#TFl`q6zj8~VK(A}|iaR9LGtC*q097PLC3j=U8XUQ2+ zu*bPp(Qkf`P%LYhlv1~LRS{b)G2L*k?Cg?+ICRV)OoiU}Dz*z(t_p_AHRDq0%3*Fi zWUVW5v@lOCLn;y4EyaQ70r`N1lxC{pJO+1yqqeH`+*-wb0uj1hT%Ms<1jI^QMzt5+ zSsQzB71*2&UVpwI#c~bZSE-q>8mzuJs6wKO^L&4meG()PK@#AGzo&3O!5G_s`^=bZ z^74CuVKQAyRHM!O2-djhSmVTD2n+-!Qj;@4S8N?e1Rp^)eU8xRnKH*QXJMjJ3fH{o zMbJwTGb?fR6)O^@O8R2&-6$v#c!AQ-FvD;avQC7xG^VAXG= zNSRa80uEhyjZ(s%cpd8k3kFz2%ri!NNhrT+kf{yhB;{CLdf5Spc~k){|pyQzRE z_&0DW&GfeuCW7=YGW=6&_X$Ue3Nh*yRf?RZ=OUWozfgP=t}0rWN!FljExHHxp^(b0 zC*lSKa%_TiR-&~-(E~&-uP@AKG>*0rRkwks+)2Q_$L1IddFw<6vEK~PL=>P6G#44k zUg}2z$}eC=#aCOFmTaPzSdDTfH;^&h5JjCO@3alXjZdM z@1fX~g`nnG`fD-1k1~yyI;7racGD827kYq7LkJjw0#vBQXX#LIcL9o^!3ZuM^pw%eWjFF*WCttj^sZy_`M9>XC%qA7C>_f1j zgR}I+vRt`;OPBP&rT+lnGT_UX23#11)eks=4cPU+#6UHU%o5b9!PaG}>kHCj4Hs2* zFG#Au9NJ=8YdltK<|Rk~>yj1Qplb0EeHP7|d6#T^f)N^5-c_Q?&Kxq!Qh?ibfkp>H z+vXQQos`is0E4Q*D`rQ&J{SU`UaHA<)Tc&%A*sAzW0-8sUj0EWTAtaAN}I*BJw@26 zsZRV#Bq-UX;!}1jyf!-3yYfMI4W?*@oNWsJ7_5P#FIwaM%c$0P=ZK%-$I<#Ri5XCC z50?gJ`>!xsvs#Ys!m7(^*n2)$HB}yw(C)(KlyDtl8E1y!U0{KlpW24S3lD^MhXJ8B z5l6V?b|M>W3w7on4G+9kE|&dF0wqX60uYo+h%fOHRK>a^&vlCFN*)OZnu=n}i;x>BO%tS6f;mO zC}2OCqFWrVSG*@~8|cBB^^$FZ^y?zw*DO{{WT4>BP7g9g+jsR;|_F@hv)-Y)W7f+PchAv7KX3h&Pt{Lg-d? zf6Q5Qjb>2oi*$q%md^5C(al!&8iWh3j0eo1r5DSe5H?mgq3R-$bv4qRdnzZVc0 z4j;D7yz_ zPWC*r`pl$V(b8JZDfoV^{{WAFrTuPPAMif6FX%y^GT3{EznE7`_ZTQw1srdr*(PsK ziCE?3sZdwrY*fgV@^^_=h3evkpyHM#l}y5;;$hcUlIk%`jVS%~EDCbk56ns^#dy7~ zOaP%u0e`uE4&)86NuoIi(2Qg$St}kQGN!;!h6+Sv!Ep;fwf5W+|bT)ZsIvAg^uv9?t&&5Gjxh0F?m7^QK`tqwabH$Dzn; zvCxSXR`JX-HkS`DIOzn6gep{WMlz*Joc$_sm*HpX19rDA@WBej$EiuHWJKN50CSjC zs~Ds*6$NTHqznb~F5u5ju~1pwWOVGPj{2_rKj5*KE?@pP{%6wR^caxTVnfh1dxML9 za!Y_LV$6DnvT}sD;$bgQes`4a8X#~ta;nnftLile5VpVEpa{KS4{;ext$~iQtzCka z`Yc@x;+37rLwe0{_^3Bd=EWTWF3MCWDnGk}!QHiGx$`obrnJ;z_PVMF@kj>N^31wc zn8<6#5zwIw^HD8Y$QX9t;w{0qVj*@n{*hfqmfOs>S5Bk0WCd!I8iS0_=@^mvG5SA8 z=>1Q%gBT&3NoP{yAqIeAF{NPUW2$i%1KxW&N3Ciw06R-`2M1}M5C<>f7la2YbVNFy zDb2*-;8#WdVIc$U3=~5dT_Q?}Dkn0dDp%5`2ix;O`jutu{6~9cYT!Gfcet&Yq5`TC z%msYJFag#G?KKL&67*4x1P=*r7=>01yh1Bi9$|k+{{V*N{YZUJrJZ3>AR=K%b%kA~ zubC6*9jujz1)0QFf|m-kN}|h~aP=#4r&?#+IJ9*d{KFMt=KlbhX2NF4Nqay|fN{h% zPAaOOCZkSHuFHsuI(D!e=yc!3#gSQhq#`X=y0*)VwSxginC8Hymfy^~BH40lv0u32 z5ze*k%mQ$I@d2Tc;qwM=EYOmOt#z~W1(!n`V))02oi4{lJ|F;g{qfQT7eSDt1RkFQxUDE?@o^E?mE%2v^W% zp$nhY&QMU%!7h~wIq5T7w+$PC5+7wVC*!!L6vew{nqUJj8p2qd;1*brQ7%x$9r0W_ zj2gpgnzB(vgOzW;`hwLdU8Wx}QDctep}u;GMbmKTQSmbcFH!ptLvg96=P_^w;~@7c z2p$Sa1Hzq4C5X9R9-!b{PKSV(;8D7ATy;?Fs{G41VZnX6%C?R~c4u;c57QF)uj^wN z>OWWL{Xy6@I|S_$u!?^a{{Vpy!kO(UY39NI08kSJT?DDY@hIRvB6c8kh!G`9i4r78 zlA=WVSI~>`kIIH!(9T*2G&DyHV2fF*@5~!vFJ^2GWqMoU0Hs|>-J|N_VxKiq!REe- zeq|5^!nMyI?k`*QsKK(=aDJEmH7)}>9#|ZADlco%_kq2q{-7oO2kCHS%YY8s9vJ_a|VSk31CRmvirNsoB*pqH4piQK`lEINVKSJtNr{n)># zH`iIRrT}NwSgUkbRwEYqdO>p4ON+tNyajBhsTrjYyN;=bR;3(JViY#b@Up-On&9y& zEgoeiiaYTC0E;vE)YmU3@_{RSf16&Q3IH4gxU%fgL9c}QhH{hOkNlVZ1RtRfp)M?1 zyxM{ZJsJ6oG_*P6_YkI)oTd_sd9)f2RLgKd*o`NrX^R4Y?{C@&TSZX6Fo|Uyr5jBh za}pRZaSj>)d_s<(M(dR{IOaOWLl}P8jG5AeL^UUDLn!dOISYa zaH#}nd>`g;4~aPyc$y6!KY}k|;J;B^UL@fU5-$n(2vcufOEq2^i{GUG02AU5Xw-@YNEuwF}5?j<$hx0%OO&fxhl-dajcXjz%{jqjg?q~jTcv1iWVD|1sA$6 z5JF0WGL$Xh^$C5dJM#j?3hInAe-k$RCR-plAU1OU0CfNgZv93ji+34$QWMl_z;?TU zy*x!?sp9L@ZWRPkf(KcPAaURL4Af=wEf6iCgU8U#CZo-F9X~I)?~qX1wdKxOZ!A^i ze*@?L06T})<^4~fz!KJRA<;_uqa54OX)rC>(rPrp9A^+PRvV}IjVV%Xc!;5Ir5f)Q z8Od$hYz4K%Mb0ym20H#Zg(aHfKrsO_G}?4UVXF6G)* z)+W5bzXGdUq5E7$_9o~Y7kjQ_jifY?3%ptqhdCc)s~9?oGNV;}5&MvN_FchptlfPF z(ToXDY*ek{5$MjuG#05`mM1`Eh86B^{{X1OSZh!;6-)TcHc(4XU*b4vNM|;W4pXj~87=tq&Zd_JAODum!_UI@rtVLS|d zf&T!Z{wLJ>E7m#e#m$zrmX9$At_lW&(KQCmUr!J#t%jHS!i%XlS5PIscB}bfFAzA3 zFlR!ncV>xOR*VV-*NfMXpxI$x68k90 z6?xMKc$}URl`B}pq0fY$sA(7Of4F$*#(-Ihba3V*R>BQ|J2JW!xRwhH z)$=QUI_beI9t`+_K@cAh_J&}#FJ?M1u?oNy&C#fvZr&B5o-?}{nW;ADDA#B-RYNk+ zUafE(!q+2ekmn`dDHw7`<}lJ%s{YQ<1FJf&y+VMerWq+I(&_-@4?_LoIuY>_ny1Ok zD640P0bH1rR>9T91hF}>_JTs!oBTpuP}UQ6o5)ppV4dj)8%f`fq52M$fk1j&=ZI;* z3;`Q0agLImHLLA)eZ0y6eS^K6UZKY(fC<6!b_Yj(za4-0tm`fb)>+h)>Q0wN+pE9K zZ&kyF9*BC;3RbrmwkQT}Tm4OA2PNEEyE<3#7Ua%1F;z5d*upFWIQjDtuO8 zg)$_0R2EoKyt9USv2a7Dd0(MS0*vpL>(o>WYrx!7bz$zJ{f)LW68Q2$jGM$kBk-RR zl2#hB@oD|dejun#ZCb$U9sPg=!pD(%L~?;cYXs(mS3+z78Tc+7&c~P(7(CTIt}cdI zRj$F^P#z{6rFFG>dEyNXT@*kAZ3k6gxtv$HEXd3Hgy^rV|u?{xt2`BN@^QB0>sRGH|g zxvqMT}tZ&6KljOb_u20qBj? z*p!P&x7V+USVW}K*o%jpLrlI3{1ItRP>JfI&&r?TZyf~>I8qcqN-nAn@qCe}bE2cI zD}7>d%o8Et&WsS~wE#Mynw1Tum`b4FF6*QVvN*K5#xFI9xd{mu@Akpl*BsQ+;k`;cWW$GlDwDYUusT z-9-|sEdv8OQ!;|Bif<56bo|qP{+!HVc&{#5Rth$Enz zLjpuMb>L=jTKhsOT7)5?HvUuE|-?N zUr5s&)COuGA0P>`!rU0>OPm)_jEQy5Oh;ow=R)s%T{_|~`K*U1_qWDl6>NYNrHifc z(pQ6_Hbs_g0*7d`o>&mQkx%(HzJaSNM`MWZn(4`6FM48r^~1+0#*7j86_x>)zza;W z=anUDeQw~$%1uPY23K0wF=`cQvj|7hpNa80(IwwIZZ7D}S+g#sg94Z63nI|atF=y! zB3QnZAx# zy=X##M@78MOWDC3I>q#X*Zv5iLbnGEQltUE^u58lh)T8^#?sH3N=?Ccwv4so;w%CM zcpPCaWzZA?)^?o?6TF5{B`*C$%@K1*1+{j$?+$H^3)@3S+E^Vlw_&UzxM@o>m>cQ| znYs<(^C;nqpm3a{BSS*FXPvpa@@Aekzh<1ee!Cb@#Ns*7W{82OfL+XNXp z(*@YaNI6OYcWF<|y&Tg+i!o7+(;i%j~GJVwu}`kI7+7 zRDUV6D|hzVegh2I;zf zCAv5iXj-=Q;QU9@lX(ZsWc@ze?Ee7m&(Qi4i0N%HoH`6W4a~(yvMdlUsixSWg7~w3 z;z>UPQ7uJ!xc3~=YdoV_rW&FwVV710HdeTbT)uL?CCv;CyZD!Gf^P*>YgxO$#6$oS zd5fK#UN_=Z=>>xYP92x2Rq-s^facXXxHzgrL^7-^W)*}5VOBn7R(R2s^3<-hX$Pf{ zRe?*DO6QM>Lr|2q+X?_OThkK-kU6uT^%p>0d`+YD+5>s&#vmtj_KhEPlo3bCC~jCcL>2RHZivP~6(TL3<}coD;jPh&e(^3`SVqh03G*k7A`b8GB+7i?D3}01zy|dI+!Kj}H~72B~*zj8{G4feo(H&P*HqL_R1( zv_FUJpKS*J0AkYMda?zLcURIITdj!hN=G7Eq4oa&u=*e1!bdID)D0{bf|8Z8oLxtd zsNk%?We+=IDm11_->3pri@t=!yBNjaxG(^Q40Vo>JylAwqnpVZ;I4xm>II|@sq$d5 zCJbxM#)Ye!M$Z)~Qsv6g-lnT`rKt1jWEvd;`BzaIMw@HMMPl_zx<2q>u{gEy%p%je z{{TywWN(kSF{EB_1Kq;jg8E(gAf?MooK>A5KnZ6H^28Q`8go$4nklwBYf{;pN9nXH zNd+i6Dik7>+_1iMJ z&ZH_wCnN`)x(wUHD;AO@u@HO)k!1fxK7_LT+70Ch$JsFvCnZC1CMggD&b z#W$n^syC43Qt3LEQn_exYhiT%0CO7$jqF^xt!5*#}wA73_t=H6k;-WUn@JubX_o#x{T$x0Is{n~lrkE;ynG%pe`6k$=MWBGlFZ~R9 zrP5qEb}5gTaZR0fDp6{<{{R8%vJT%lrpvx#W+x~PL3!;H!T>46iKhf7ktA6(&TD*r zR#h1=l&#-tL2&G2R2mkwZ{2}dR2d1UzT1KT;YyuEaC`W;eZ)ksVXwdb-oMGvuIFH` z=qLcJWWTfutGK_+Y<;mM>#wG7mJe^vMy3`ELxQ|PH};3)GZeWzrhP(~Z*G_2iEE#~t~o?16kHa5WspWTsZ~DRM7ssnu=s*1?;KmUS!&0D>f+cf8b0a_ z26|G@v|1gdxPwr%WyxJ>sKv<2qt)srA!5+J*o8v!M$AM|v!}S?!uKWiTkmsiN9nXs zty)wl2KX&;DjURrEW4)^{v!1IAL0$9>Qab4^A^}yuo-xl!iB++Ly{S2d@R6dJj^U* z%gtb;uC822igkSjP#jIy?!vOTyXyF0DmXHHkkbf0!%rEw%cH0-xN4BjUarl$`X9B7So z_l%(h$H(mBb4I$qFM$CjaSH0Eu3{6KAuetKYkrS&>1bo{7JN(4(GjBAOeJ|Hp)Dp| zc(NnY!%1;+)uc$b!kJGtMgyeoijUI|Wc;|%4ep-G(a zli&lK5fikPW7FRmz@%-3WK3a2KKp#VpBUM9B6LZ}1pMlVhu`FOp;3fr$}XVA)ImuUyQW?t+$wKj6NeYR#T##;+_kJb zZ2R6Rx5Ba%?ybzFI?iF{)YdZY%@uMU1;(6Gube=dhO1TKyxTmCUBdbJ+2vBC>0cEg5)9SUK-jz2p>ZhlM>dikuy4{3K zaY-E4mrlJ9EgAs@`2U@aDT`9nP#b!uC7Vn|N$M7w4NVBkiE+*--nTN8$7@|gSNT0u`R^t$H zjvmYak77S+CnD47cE{(J^7PN(;n*c|Gf7pW2vj7@AMt$wczY0+E8;Rih~h|RUZ6Q6 z5TnE3S1GnSi-c*=b1dAzSyZajKb;vBkn1T(b%1 zSW_%B@-$3p1&v1ASWZ8v7Dnl_AG!?Wq^FuEy(103rYwNPvNagjM|ZVM3D(S(ql@mS zdnEaYhKVi6VOR3`FLN_5a^t<|=*|`H{|jSp&Pr5lpI11!Ql5N!YEu$v(#)IN{e^~P zQS@uam{zgwbTZQpP0W?!7|VJF&SUzb!51f_D0~4AO=@Lmv7x4)y~*+v@y@TME6jcz^$ni zUdm@5bJSSOsSn5*$M#P0J*8%goImi{U0Aez^&lc*_a*hg7j<`k*A{tNK@Dv^zyo@mfnwV%-n|peV@-rZoy0rn(}t2ixMazbAv=)n z-o#7uM zPnd2_!Z``dYk06tQV01Qdr(BqZdcZ{#(ly7865YC$G>XPuZvtmfG&aSb$-_ztJ>qQ7%2^ZL+pB?B zZyT$p8KlUzo@2|j97i76l8_Q=4gJV|%ROUj!J~Xgl<^{QeejKn zsI-$xiXp`@h!l(c3qEeGVyE(M5sYmQ#uZ@`zc`&ZVYu*xj?~;5LhmYTD|p1>m!(4! zjJBGFOe?0Q9(at$*oBWjNksmFcDfW6jU}TN>nGvbUC)sXXOVA5U7$r~1+LxhP{-t> z%k$=(GKfTsE@%vmO8Q<5q_nr>@17zsu4_+FGi6n<9dE0RrHt(jz8hwTR$Op=WU9de6`(gIPH{I?`q$ zyzJx6$V@`RN9GuK=x5G;ZHlUshuz*QUp^DJ?;Z}Z@;12See6!anuQ^%t>QWN)T7nY zsF`v==HyM^65c!I@x(S1pA?mWbOgs5Zm5|5@}ODQ<9fs{O=7vqP;27+9M|wFiWPiX7>I7BFqyX7tZiM58^qu$Kp1ezyBZ&$?`K_Nox4+oXHs_ z1AQ~I%9Yz)5Y%mh%s3hR!Ffs)?Y(u6t^Ki5R;~B<1+lId_yIO>!Lbn3C{nO~-R`ap zQ{fQW*wn|6P|1wx?2%5{D}OsR@58a1dM__o(VYFWa;@ept|Sr86j^-|8RR5MM%QIO4yEvrAzE*`%Ebn;B5D)8sufdR6y*%YuiG zh)?FOm-R_eCUJH-J83UKi|?B#tcSx#SN%Ld)Uu<;m{LRv-C6>7I2bz2$FbfSIQk~4 zHLB0N5^%lvh~sobo6IkSI^K;L)y?@vZ?lcF9j#9YE<}Th#rJn{rR_L*D=$}Zy_rDP zsK2GZ?4|0mE9mX2h)_mhhh@cM${dV&|h#KJ=cVRJ(SkHz6L;OnKx{vY_+vU*`)Ee zI5g<*NN}pF_yp{Ta5nnmSZs{`Vk%EmB_ew?#Sc11s%Dz?eebbu?0MX#-H;6Ww2ljiY5kSy2v3 zvUkG`_ygE8Bf?u+dHHH2)R(s#4nDws5z(r#PvrE@PyhUqf9~ z&esU5WM%k?S9Bp0fpHO8wz82Y>taTtuE4&I?E|_%1^nc~9qv@RtY3#D-$GsMG#FL7 zYoW0^M)d(B4Vb+hLvzzx%i_Cd9?oKSYXaI5;gP+Ve*h-qLchCqfYPCBo?RH%hh!WT zkKe;qaE66~*VUqr@9Qe0o`&t#Usaye((P=0_~}9r$M+Aw8SE6$Ge*p5s{6Dsktgli zZ227l)uU2X=DgrqTSE)Cc07Ny_@HJzp9ES0j%H z{E+15{R2??rB-apT1$1t7s^6Pi7_-(*EmbnuD#NBKDL>oAy)2iNyazw; z&KbQtcnW3`^|Yg*uo_%lP`@C1OcO=1>gktN+WNKC**hgiUZ#Dw=M9+5M@;Wt-Z_Y< zb<^U*iA3*aa z7>YA&6fp8YhgtPpf7bcYtK$0Y=hb(wFJQqxtZzT^-`d<1Ato@!=-0;RKaWY)Th(-{ z9uXkV0tnuh@iqPC2D(3f_@u+%$B)`8c!bI}um=Y*2&TDhKIpa`(~tCeyu# z_`P~uL%}H{ojEQ`@29i$=ZCVCyy1MOm|w$y-+nY=k4VU!6Mr*GFk7z}eKv@vSD4{n z`xhY?i_09)c+9u>N#ITEph-kI@{LsUDXMAC`uQKg!Rx5k)jezyN!RTb)1@>qH{S_G zfrut<-YCWd%kEu}3?Q*yFQ9EpVi#AI*FiH|q!9w!7sOJCMklvl)pk|@7IxN5Nfq5O zG!+2!O&ckfUa_sA+sXw4uQ4Ls_mpq*^h6=Bxj6Wo(V50R#+PtUk*^-49P~yRM|xea z&%-?GoDvz)&~=}0+$;C>_KT;!>o<{T_|m>l!C$-)msf{6v^;q|Z^z&y{5ZYq;g(e5 zS>B@QZ`XqMBFJ2?c$gY*>rEJiYMhQs2qFF>vZ()NTz!DI`L#fx(MwDC)IO<8Pd|5J zEcKAGyAx)i;cpft;gNFOTHCP-^wHwM=$XODl3stl4B)fTQJUWutnGpIzLK6kueYm6 zGi>cg-7Sdx(p>0qfDAX(B;MUv)8VfoXe4G-+S6}2RTbUUqOFD?eeeE<5{gMt6=FHa-XXEEoB5A~9ViVuj( zhmlX$@^0rcJu5VS0N$Z%iVxeZw?)tUR&5yDf0wyfO)1h*{?cwrQKN4c+*ZtIs2=ON z`iOCik!EbuXI;M&f*B)Ha7?6dFM(@NC@UDy=imn{?r7X_gY4?5r( zr-Y&ucYjyclh-K;nr>%!vGB>BY|EU3Fm-|pkRgs7d5`7~TyR}*e7M=@JyF|MQFH>P z%l6@ydpQqz)m)0#lx@4($9b>rq+W^Eb-W`SbTvY{(4P5ipQxqzZgvL+Hlfl z#=SS%M2tTBIUm{nnCCJko~|DLb!CcG^ihiNO^iRMb0m8GytW~8_-7#?C80G}d`>DMs3308ypU}RNq2^D%_(Y-@#1AH@R4?>I zXf`x`9sME`-0}3D(~#Ss^aateAzOax^b;muSPWf|Pvg{OO=mk9`ilg)K|gTWR_7NH z>cWiM98h7+b2WC}%fg55bY;&wyQd!9G#R7*(u(J9nQt65;x&FtDYRqGrAiv|$Z){q zsxE+mS+o%<6jeN3I8V0e-h=rD8;GCRXILJ?1MYUzsHZ;=0jSkhANKcTO-bIST(B-5 zY!6i4s*YA_(9V+y^w)mNK%Y7=mig5@M;fK$YmZ$a&R&b9#%2Q`Md%OWB`*snPS2XR zkItntXQ)uE(Q#`;0qS??e%RVcM7$Pp>u?`c3AYWQ11n*ks` ze>Uq$jqp?V@@+bI6|6^NwI0yw+^k3ncqLsFAxeH6XHR!SzO1SC!U>~@NsPD#iXPjq zV3L9N>Xm|M7P7`Fj%^q$9j;;dE%JSU+_Ot9Q)N)H+wevn~7f(BCB9H^6+?IGTY7K1Kd}} ztPPLWuS_telB%9WcfAc`sHb@Gxpb3d`B=Q1W|mhz_VXWrSg?8pL230gv_U#qKO>TuA2wO zOeVR5Z|Vo>Z1VVoA_C4@EXrCcS>`U-gL0i3C_zMXs; z!!+XWqY!O6*jyI`+n*T;*V_HrQA-0mFG0_j2-QPpWk0~Ycgv_uG8v+8USJQPYjz~Q*GA2mprj-m+Oo$|8N0h-}7yI9(aj4Oo4nA?754I zS7>56m)J^yv&|PbUE=WdvoovLK$N&Ybe3TXLJ4L|`0lcJ zPFGpf&SSJ7U|1%uf{Z>vALpeSx!#YsFj&pB=KV171~~aN+d*c#=n}`!C1&RL;9mvK zh0>b_h9i+E>+d;b!yUxsoTR3w8gC%qT-&F6W^L%Fn-4^UuYV_YZ6K0 za9m$boGJH22RDK7I5fU!4<$1q*%H<3&-pZ>*{{-Nc*0xUzC9A3g3)-89dDQbgZsx|Be#Dow= z;I6gX+8=;gY>CWo2c zP?%?YVwa-{kBon~Bc0B!@z9Gd^s3$Sek^O(%x+V=_>5!fCSMZE?AO4fKE+^M*ZtOV zHQ1F;42{yb=?{QhrtP9M|~>Y3k$t1~aXLM zJ~IlQI0~yIy&b;(blv#4>eKD)(OZBxxQAt{r%ivnj~~5n)B4<)4B<^WGu<+%GLO~{ zj3!YcO(vTQr{xk!>eXl~tt+`2-0wOM_4vy@M&#{FOQy-pCCzC)x4&1;$?luUFn}Wo z?&7<~!|KmIUipy!^s^b@dSQ9U)6*6Q;ti;;&on)1bANS;n7rk+vyE+&&cv|q!%UG2 zV;GAA71JBbf5*GpbuKU!RjveeO!snSgiLrK;d{ou+2RPW=KEGc&-cV9%%n7(gD5^f z`g^B^-3%>=;uCbx`Z0*s9}S$js`ry+Wr`Acvm>$$Xy{ozG)ucD-N?%NgiGKYuH>p1 zkp!Oii{UWfi%?3K8;e}KZ2I4d= zHo=vb0L7Ir4Nv;KU!;Aneh%L!6-Tfv+%wsj-dm+eoMp19m(ZWz3_PMaj^0RnKMI+T z?J>d)-#hWxZH>_eMsdube438$TvK9OSnBUO9rmoeCPOizQ_?{@=m=o^n3B{^{*m(W z{YDfin80KbliWX{bTFyE^I|e zd&>YT@8k9R-M5L_>}L4u^e42Ixr+{0xbD@Yo9!N$Jyoc_VvD4GEnDB7FT*MPfmkd% zq<|&Qt%>mQ&*~qAHS57saj9RrL~k=aN6G6_`|E`bd=l(I1aj=uky#1Ww9-?A{;GH@ zqCqP9g}8-bxfgv1m+O>#v?4MgH$kS?Z{za7S<$o6AjQaaM=4yB}!43{8eox z0)G$Hfyx((LjxMJFI|CVP!WP$xRbIaZI| zU9-7F9LvTMpS--3^H*ifqW|huG00)8CDY1@c31|7^|@e8{f5xG0ukM; zXgY1@Wvt>tz)clK+)h2Er(OCHTUjz6ovj&ulK z&$*oeJ$ycsJbm^s_y>T1o-$;N<*tczoPIV_d6jW!t_VZ^iJS8ldPv$mD{~~>0*3)^dxq#A0JEPJtpAu-G z%uay7syv~giN@<&$>uv2%EHoIn*dT26`fYtW0D#wf9=5e7R(aVnRUjA5Sj z!R%(!Jb-W^WnLzu4H(vk4(iA*lD{^Ho}C)rk*PUq`99mlOU-|0JA(@AM97e1&xDA} z*M)KAv~`Rse>?DoFlsYM#&J0aMVhSFCF*x4(VCmhLjMHf9E$#ZRoO2g6$rOA&4V!lS1hFXU60|c~|Vw=a*O>J#}D(cP^ z$-F)(A|s!mi|4UY2&>N(mRPc++OjFx8g=j=2}vCs2o&Gd!ex~(UCQx&kNY8o;?}ld zxT%HAyv(Tt1hMQpPtjios=*}Rien_Nn!7;#pfi%EM_5@ufIj%T0R4+d=NZGndNPjq zn_$vFq)s=6fcTsuT^Ng|D<&Ct4JJ?XN*F%PR<{3Sm#Wst=3k<8Ko46Ka{V48DQ6Fn zYu@v?v!}lT+1^a@OPA?-qdi~AMTC+HDABI7LT^>~Y@Brq zT`U1gxeF4GzL%>q*GvpE%Ym{M&J8-R@{~Zt{X64TMrO2uWHUaX5jPDI29FpG7{E5E zS0Mp0m94wg;w0uyDX*|pp(hwow>bJtx;Orz*WB^D(68!GODTu98PeDB zD)uWW3CVq3r0Skwan*bwXc>YjX^-Xy+y^H2l5uly_%-d)p;8f|TK9?qJ-l)I!iCAn z{rC`3>M-m2q0#Y%v4Gf}JkH@6L=pGEgR7)=X2dCSFsq+3BcycS;V9@Xhm0q~)DQn6 zKwLpxfW6trSQZ`dO6A933$wV0;&GeO0Q|Y#PcJGdy<$gd#O5pNR+e0`nwj!62dUQl z2WBPo_x_+BZk#in%HWrg67+&P&B- z@*PKB997Z={>VZ^t>hqJZeDA+Ku1zwLc|MwjBJ=dn?UlvtU`Y@sbooU*Q)OV@Afv5wreroD1anYZ<@^k zcR06xi{C0M;zUnJa}&JMThnwC{sEg!fp8Ufnas)#i%#6T$d~j&+kmun=5-kx<-v>G zPOwfZ^58tqD&oEs3Ix!99WEpE-nwMB;Dedm4<>w&-D*YBaw}9Do}pwow(-3VQeBlY zp3gGo7o64S{mysH751&+>VjYT?Mz%A^hiI_fJ25S3ybd4NM2>b8l)!qid9D(_Tj9HdOZoDrTG zKuf5L5$%RFwq5*Ev>@0Y-<5{>lK52O1HQ_Ie^mJU;?9+Y#AI*A7y9^4?wy-Wx~sLD z&YbmqPAMr!$9-LnZuswyL92=&foUal>PkI2d^jyob-s8vh9-stblvbs)%~tC> z`UwUXN?=>=GVMt44_2Gx+BOEX#wZ6J2Fhfr*ukxCrVTl-_DxN})05)&-Pg2X^!k4_ zd$eikGWe~ie<$hf-r2>-2}i&6_aKo6ucBWa;}Mu z7I`#o-cLA%d@hQ(jOlu-eHG=&%cLL{Hcn7j@e2Sa!v70(G0qCTVD_76VCJvlE4BAF zrkYKwK(zZQZX<2(#yFKG6S>kV!(3A*_m9j8e_6ogPIAmdh^uss1#fe+MOu;0QBTWJ zEHuU<-+urBTpi{a*<&Z5_=IanpEJq&{nxj*x!UTl7L_~V-3Xg$l&g!J<20c0y# zV*n{lse#?#t&Mc;(5zjKv-6NAXO6lKEf!zhDPOa(3gj~mZbHxNRT*X0oslvdi`Uuo zc<26X==D~f%Q4?x_5%Q%AGM+Cp(iA<9M>_DPn)~QG*f=PUz3#Hl!$cp_W#H>xI_XW zUSvU17P&xUip~aE#h65a2a?i*x-I=yXfE;&Pz>{Nlru?tNx~_~Ki@P|FfjFZRfL>L zPyi*QC;$x=AQ~wE6B82v>5NnX{7WEuid(x`X?VK1d+}MhdD`&$xj4HZJMkl=2%|Uh z%Nb_|!lDJ_N*Y@xF`!59rL6hBQ6+bGDdCDjp8c3;J z-wEP=_aQN@RTwnEgU~OKunr`lAtC;x6_@92J!1Do#mW*V($Sf42=_>7wEgFWKi){{ zxX>l#$TG}sS!a%%j>|x+pFn>Q9L495?CIIiah6{uUyq)J5xTP$_w3yfxQ^Ee?WMKb z-y9cr-IYq87+gKw|9_5z|H{$A-TiNhAIAjnmnc-myiI0#W^zd32Lq4g~fx zr#UUeH`nunUrw%gPtSKmRt{`iq~ahN=T-(D=&%ueTGr5Ct(*M@s@TanSkmWs%%@AT zTR?xz5t-`Ko-cwi^{Wyi>NNR62t_5Am1^NO1CKlaj@jUmb*pL593!`YAE( zKe10SV$6TQ|4-)sD&)TwL~ncl4gV8nRTcms_%}(fu>K**%iG`C#_PWoqdI}UGW)3r zblCsNqVkE<|FZD1@%DCbwfnE|f2~(6|CV*`K#Q&~UNp_U~w{rd`{9jGwZ%hAYVBz=|_&@AkLj{EXcM7bhOYKPs_fKg6 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 + +