diff --git a/cdi/pom.xml b/cdi/pom.xml
index 0c14df6e73..1d14e3c267 100644
--- a/cdi/pom.xml
+++ b/cdi/pom.xml
@@ -14,6 +14,24 @@
+
+ org.hamcrest
+ hamcrest-core
+ 1.3
+ test
+
+
+ org.assertj
+ assertj-core
+ 3.10.0
+ test
+
+
+ junit
+ junit
+ 4.12
+ test
+
org.springframework
spring-context
@@ -42,4 +60,4 @@
2.4.1.Final
-
\ No newline at end of file
+
diff --git a/cdi/src/main/java/com/baeldung/dependencyinjection/application/FileApplication.java b/cdi/src/main/java/com/baeldung/dependencyinjection/application/FileApplication.java
new file mode 100644
index 0000000000..2ae8ac9621
--- /dev/null
+++ b/cdi/src/main/java/com/baeldung/dependencyinjection/application/FileApplication.java
@@ -0,0 +1,18 @@
+package com.baeldung.dependencyinjection.application;
+
+import com.baeldung.dependencyinjection.imageprocessors.ImageFileProcessor;
+import org.jboss.weld.environment.se.Weld;
+import org.jboss.weld.environment.se.WeldContainer;
+
+public class FileApplication {
+
+ public static void main(String[] args) {
+ Weld weld = new Weld();
+ WeldContainer container = weld.initialize();
+ ImageFileProcessor imageFileProcessor = container.select(ImageFileProcessor.class).get();
+ System.out.println(imageFileProcessor.openFile("file1.png"));
+ System.out.println(imageFileProcessor.writeFile("file1.png"));
+ System.out.println(imageFileProcessor.saveFile("file1.png"));
+ container.shutdown();
+ }
+}
\ No newline at end of file
diff --git a/cdi/src/main/java/com/baeldung/dependencyinjection/factories/TimeLoggerFactory.java b/cdi/src/main/java/com/baeldung/dependencyinjection/factories/TimeLoggerFactory.java
new file mode 100644
index 0000000000..86916fa8c4
--- /dev/null
+++ b/cdi/src/main/java/com/baeldung/dependencyinjection/factories/TimeLoggerFactory.java
@@ -0,0 +1,14 @@
+package com.baeldung.dependencyinjection.factories;
+
+import com.baeldung.dependencyinjection.loggers.TimeLogger;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import javax.enterprise.inject.Produces;
+
+public class TimeLoggerFactory {
+
+ @Produces
+ public TimeLogger getTimeLogger() {
+ return new TimeLogger(new SimpleDateFormat("HH:mm"), Calendar.getInstance());
+ }
+}
\ No newline at end of file
diff --git a/cdi/src/main/java/com/baeldung/dependencyinjection/imagefileeditors/GifFileEditor.java b/cdi/src/main/java/com/baeldung/dependencyinjection/imagefileeditors/GifFileEditor.java
new file mode 100644
index 0000000000..6b51d64a33
--- /dev/null
+++ b/cdi/src/main/java/com/baeldung/dependencyinjection/imagefileeditors/GifFileEditor.java
@@ -0,0 +1,27 @@
+package com.baeldung.dependencyinjection.imagefileeditors;
+
+import com.baeldung.dependencyinjection.qualifiers.GifFileEditorQualifier;
+
+@GifFileEditorQualifier
+public class GifFileEditor implements ImageFileEditor {
+
+ @Override
+ public String openFile(String fileName) {
+ return "Opening GIF file " + fileName;
+ }
+
+ @Override
+ public String editFile(String fileName) {
+ return "Editing GIF file " + fileName;
+ }
+
+ @Override
+ public String writeFile(String fileName) {
+ return "Writing GIF file " + fileName;
+ }
+
+ @Override
+ public String saveFile(String fileName) {
+ return "Saving GIF file " + fileName;
+ }
+}
\ No newline at end of file
diff --git a/cdi/src/main/java/com/baeldung/dependencyinjection/imagefileeditors/ImageFileEditor.java b/cdi/src/main/java/com/baeldung/dependencyinjection/imagefileeditors/ImageFileEditor.java
new file mode 100644
index 0000000000..d524a5160a
--- /dev/null
+++ b/cdi/src/main/java/com/baeldung/dependencyinjection/imagefileeditors/ImageFileEditor.java
@@ -0,0 +1,12 @@
+package com.baeldung.dependencyinjection.imagefileeditors;
+
+public interface ImageFileEditor {
+
+ String openFile(String fileName);
+
+ String editFile(String fileName);
+
+ String writeFile(String fileName);
+
+ String saveFile(String fileName);
+}
\ No newline at end of file
diff --git a/cdi/src/main/java/com/baeldung/dependencyinjection/imagefileeditors/JpgFileEditor.java b/cdi/src/main/java/com/baeldung/dependencyinjection/imagefileeditors/JpgFileEditor.java
new file mode 100644
index 0000000000..97adebc1af
--- /dev/null
+++ b/cdi/src/main/java/com/baeldung/dependencyinjection/imagefileeditors/JpgFileEditor.java
@@ -0,0 +1,27 @@
+package com.baeldung.dependencyinjection.imagefileeditors;
+
+import com.baeldung.dependencyinjection.qualifiers.JpgFileEditorQualifier;
+
+@JpgFileEditorQualifier
+public class JpgFileEditor implements ImageFileEditor {
+
+ @Override
+ public String openFile(String fileName) {
+ return "Opening JPG file " + fileName;
+ }
+
+ @Override
+ public String editFile(String fileName) {
+ return "Editing JPG file " + fileName;
+ }
+
+ @Override
+ public String writeFile(String fileName) {
+ return "Writing JPG file " + fileName;
+ }
+
+ @Override
+ public String saveFile(String fileName) {
+ return "Saving JPG file " + fileName;
+ }
+}
\ No newline at end of file
diff --git a/cdi/src/main/java/com/baeldung/dependencyinjection/imagefileeditors/PngFileEditor.java b/cdi/src/main/java/com/baeldung/dependencyinjection/imagefileeditors/PngFileEditor.java
new file mode 100644
index 0000000000..5db608539c
--- /dev/null
+++ b/cdi/src/main/java/com/baeldung/dependencyinjection/imagefileeditors/PngFileEditor.java
@@ -0,0 +1,27 @@
+package com.baeldung.dependencyinjection.imagefileeditors;
+
+import com.baeldung.dependencyinjection.qualifiers.PngFileEditorQualifier;
+
+@PngFileEditorQualifier
+public class PngFileEditor implements ImageFileEditor {
+
+ @Override
+ public String openFile(String fileName) {
+ return "Opening PNG file " + fileName;
+ }
+
+ @Override
+ public String editFile(String fileName) {
+ return "Editing PNG file " + fileName;
+ }
+
+ @Override
+ public String writeFile(String fileName) {
+ return "Writing PNG file " + fileName;
+ }
+
+ @Override
+ public String saveFile(String fileName) {
+ return "Saving PNG file " + fileName;
+ }
+}
\ No newline at end of file
diff --git a/cdi/src/main/java/com/baeldung/dependencyinjection/imageprocessors/ImageFileProcessor.java b/cdi/src/main/java/com/baeldung/dependencyinjection/imageprocessors/ImageFileProcessor.java
new file mode 100644
index 0000000000..1527108568
--- /dev/null
+++ b/cdi/src/main/java/com/baeldung/dependencyinjection/imageprocessors/ImageFileProcessor.java
@@ -0,0 +1,42 @@
+package com.baeldung.dependencyinjection.imageprocessors;
+
+import com.baeldung.dependencyinjection.loggers.TimeLogger;
+import com.baeldung.dependencyinjection.qualifiers.PngFileEditorQualifier;
+import javax.inject.Inject;
+import com.baeldung.dependencyinjection.imagefileeditors.ImageFileEditor;
+
+public class ImageFileProcessor {
+
+ private final ImageFileEditor imageFileEditor;
+ private final TimeLogger timeLogger;
+
+ @Inject
+ public ImageFileProcessor(@PngFileEditorQualifier ImageFileEditor imageFileEditor, TimeLogger timeLogger) {
+ this.imageFileEditor = imageFileEditor;
+ this.timeLogger = timeLogger;
+ }
+
+ public ImageFileEditor getImageFileditor() {
+ return imageFileEditor;
+ }
+
+ public TimeLogger getTimeLogger() {
+ return timeLogger;
+ }
+
+ public String openFile(String fileName) {
+ return imageFileEditor.openFile(fileName) + " at: " + timeLogger.getTime();
+ }
+
+ public String editFile(String fileName) {
+ return imageFileEditor.editFile(fileName) + " at: " + timeLogger.getTime();
+ }
+
+ public String writeFile(String fileName) {
+ return imageFileEditor.writeFile(fileName) + " at: " + timeLogger.getTime();
+ }
+
+ public String saveFile(String fileName) {
+ return imageFileEditor.saveFile(fileName)+ " at: " + timeLogger.getTime();
+ }
+}
\ No newline at end of file
diff --git a/cdi/src/main/java/com/baeldung/dependencyinjection/loggers/TimeLogger.java b/cdi/src/main/java/com/baeldung/dependencyinjection/loggers/TimeLogger.java
new file mode 100644
index 0000000000..44223d7e5d
--- /dev/null
+++ b/cdi/src/main/java/com/baeldung/dependencyinjection/loggers/TimeLogger.java
@@ -0,0 +1,19 @@
+package com.baeldung.dependencyinjection.loggers;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+
+public class TimeLogger {
+
+ private final SimpleDateFormat dateFormat;
+ private final Calendar calendar;
+
+ public TimeLogger(SimpleDateFormat dateFormat, Calendar calendar) {
+ this.dateFormat = dateFormat;
+ this.calendar = calendar;
+ }
+
+ public String getTime() {
+ return dateFormat.format(calendar.getTime());
+ }
+}
\ No newline at end of file
diff --git a/cdi/src/main/java/com/baeldung/dependencyinjection/qualifiers/GifFileEditorQualifier.java b/cdi/src/main/java/com/baeldung/dependencyinjection/qualifiers/GifFileEditorQualifier.java
new file mode 100644
index 0000000000..3660aad15e
--- /dev/null
+++ b/cdi/src/main/java/com/baeldung/dependencyinjection/qualifiers/GifFileEditorQualifier.java
@@ -0,0 +1,12 @@
+package com.baeldung.dependencyinjection.qualifiers;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import javax.inject.Qualifier;
+
+@Qualifier
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER})
+public @interface GifFileEditorQualifier {}
\ No newline at end of file
diff --git a/cdi/src/main/java/com/baeldung/dependencyinjection/qualifiers/JpgFileEditorQualifier.java b/cdi/src/main/java/com/baeldung/dependencyinjection/qualifiers/JpgFileEditorQualifier.java
new file mode 100644
index 0000000000..c8a007bcab
--- /dev/null
+++ b/cdi/src/main/java/com/baeldung/dependencyinjection/qualifiers/JpgFileEditorQualifier.java
@@ -0,0 +1,12 @@
+package com.baeldung.dependencyinjection.qualifiers;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import javax.inject.Qualifier;
+
+@Qualifier
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER})
+public @interface JpgFileEditorQualifier {}
diff --git a/cdi/src/main/java/com/baeldung/dependencyinjection/qualifiers/PngFileEditorQualifier.java b/cdi/src/main/java/com/baeldung/dependencyinjection/qualifiers/PngFileEditorQualifier.java
new file mode 100644
index 0000000000..51d2fba315
--- /dev/null
+++ b/cdi/src/main/java/com/baeldung/dependencyinjection/qualifiers/PngFileEditorQualifier.java
@@ -0,0 +1,12 @@
+package com.baeldung.dependencyinjection.qualifiers;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import javax.inject.Qualifier;
+
+@Qualifier
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER})
+public @interface PngFileEditorQualifier {}
diff --git a/cdi/src/test/java/com/baeldung/test/dependencyinjection/GifFileEditorUnitTest.java b/cdi/src/test/java/com/baeldung/test/dependencyinjection/GifFileEditorUnitTest.java
new file mode 100644
index 0000000000..3b148049b5
--- /dev/null
+++ b/cdi/src/test/java/com/baeldung/test/dependencyinjection/GifFileEditorUnitTest.java
@@ -0,0 +1,37 @@
+package com.baeldung.test.dependencyinjection;
+
+import com.baeldung.dependencyinjection.imagefileeditors.GifFileEditor;
+import static org.assertj.core.api.Assertions.assertThat;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class GifFileEditorUnitTest {
+
+ private static GifFileEditor gifFileEditor;
+
+
+ @BeforeClass
+ public static void setGifFileEditorInstance() {
+ gifFileEditor = new GifFileEditor();
+ }
+
+ @Test
+ public void givenGifFileEditorlInstance_whenCalledopenFile_thenOneAssertion() {
+ assertThat(gifFileEditor.openFile("file1.gif")).isEqualTo("Opening GIF file file1.gif");
+ }
+
+ @Test
+ public void givenGifFileEditorlInstance_whenCallededitFile_thenOneAssertion() {
+ assertThat(gifFileEditor.editFile("file1.gif")).isEqualTo("Editing GIF file file1.gif");
+ }
+
+ @Test
+ public void givenGifFileEditorInstance_whenCalledwriteFile_thenOneAssertion() {
+ assertThat(gifFileEditor.writeFile("file1.gif")).isEqualTo("Writing GIF file file1.gif");
+ }
+
+ @Test
+ public void givenGifFileEditorInstance_whenCalledsaveFile_thenOneAssertion() {
+ assertThat(gifFileEditor.saveFile("file1.gif")).isEqualTo("Saving GIF file file1.gif");
+ }
+}
\ No newline at end of file
diff --git a/cdi/src/test/java/com/baeldung/test/dependencyinjection/ImageProcessorUnitTest.java b/cdi/src/test/java/com/baeldung/test/dependencyinjection/ImageProcessorUnitTest.java
new file mode 100644
index 0000000000..8b5fa409c9
--- /dev/null
+++ b/cdi/src/test/java/com/baeldung/test/dependencyinjection/ImageProcessorUnitTest.java
@@ -0,0 +1,70 @@
+package com.baeldung.test.dependencyinjection;
+
+import com.baeldung.dependencyinjection.imagefileeditors.GifFileEditor;
+import com.baeldung.dependencyinjection.imagefileeditors.JpgFileEditor;
+import com.baeldung.dependencyinjection.imagefileeditors.PngFileEditor;
+import com.baeldung.dependencyinjection.imageprocessors.ImageFileProcessor;
+import com.baeldung.dependencyinjection.loggers.TimeLogger;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import static org.assertj.core.api.Assertions.assertThat;
+import org.jboss.weld.environment.se.Weld;
+import org.jboss.weld.environment.se.WeldContainer;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class ImageProcessorUnitTest {
+
+ private static ImageFileProcessor imageFileProcessor;
+ private static SimpleDateFormat dateFormat;
+ private static Calendar calendar;
+
+
+ @BeforeClass
+ public static void setImageProcessorInstance() {
+ Weld weld = new Weld();
+ WeldContainer container = weld.initialize();
+ imageFileProcessor = container.select(ImageFileProcessor.class).get();
+ container.shutdown();
+ }
+
+ @BeforeClass
+ public static void setSimpleDateFormatInstance() {
+ dateFormat = new SimpleDateFormat("HH:mm");
+ }
+
+ @BeforeClass
+ public static void setCalendarInstance() {
+ calendar = Calendar.getInstance();
+ }
+
+ @Test
+ public void givenImageProcessorInstance_whenInjectedPngFileEditorandTimeLoggerInstances_thenTwoAssertions() {
+ assertThat(imageFileProcessor.getImageFileditor()).isInstanceOf(PngFileEditor.class);
+ assertThat(imageFileProcessor.getTimeLogger()).isInstanceOf(TimeLogger.class);
+ }
+
+ @Test
+ public void givenImageProcessorInstance_whenCalledopenFile_thenOneAssertion() {
+ String currentTime = dateFormat.format(calendar.getTime());
+ assertThat(imageFileProcessor.openFile("file1.png")).isEqualTo("Opening PNG file file1.png at: " + currentTime);
+ }
+
+ @Test
+ public void givenImageProcessorInstance_whenCallededitFile_thenOneAssertion() {
+ String currentTime = dateFormat.format(calendar.getTime());
+ assertThat(imageFileProcessor.editFile("file1.png")).isEqualTo("Editing PNG file file1.png at: " + currentTime);
+ }
+
+ @Test
+ public void givenImageProcessorInstance_whenCalledwriteFile_thenOneAssertion() {
+ String currentTime = dateFormat.format(calendar.getTime());
+ assertThat(imageFileProcessor.writeFile("file1.png")).isEqualTo("Writing PNG file file1.png at: " + currentTime);
+ }
+
+ @Test
+ public void givenImageProcessorInstance_whenCalledsaveFile_thenOneAssertion() {
+ String currentTime = dateFormat.format(calendar.getTime());
+ assertThat(imageFileProcessor.saveFile("file1.png")).isEqualTo("Saving PNG file file1.png at: " + currentTime);
+ }
+}
\ No newline at end of file
diff --git a/cdi/src/test/java/com/baeldung/test/dependencyinjection/JpgFileEditorUnitTest.java b/cdi/src/test/java/com/baeldung/test/dependencyinjection/JpgFileEditorUnitTest.java
new file mode 100644
index 0000000000..4f3954c0bc
--- /dev/null
+++ b/cdi/src/test/java/com/baeldung/test/dependencyinjection/JpgFileEditorUnitTest.java
@@ -0,0 +1,37 @@
+package com.baeldung.test.dependencyinjection;
+
+import com.baeldung.dependencyinjection.imagefileeditors.JpgFileEditor;
+import static org.assertj.core.api.Assertions.assertThat;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class JpgFileEditorUnitTest {
+
+ private static JpgFileEditor jpgFileUtil;
+
+
+ @BeforeClass
+ public static void setJpgFileEditorInstance() {
+ jpgFileUtil = new JpgFileEditor();
+ }
+
+ @Test
+ public void givenJpgFileEditorInstance_whenCalledopenFile_thenOneAssertion() {
+ assertThat(jpgFileUtil.openFile("file1.jpg")).isEqualTo("Opening JPG file file1.jpg");
+ }
+
+ @Test
+ public void givenJpgFileEditorlInstance_whenCallededitFile_thenOneAssertion() {
+ assertThat(jpgFileUtil.editFile("file1.gif")).isEqualTo("Editing JPG file file1.gif");
+ }
+
+ @Test
+ public void givenJpgFileEditorInstance_whenCalledwriteFile_thenOneAssertion() {
+ assertThat(jpgFileUtil.writeFile("file1.jpg")).isEqualTo("Writing JPG file file1.jpg");
+ }
+
+ @Test
+ public void givenJpgFileEditorInstance_whenCalledsaveFile_thenOneAssertion() {
+ assertThat(jpgFileUtil.saveFile("file1.jpg")).isEqualTo("Saving JPG file file1.jpg");
+ }
+}
\ No newline at end of file
diff --git a/cdi/src/test/java/com/baeldung/test/dependencyinjection/PngFileEditorUnitTest.java b/cdi/src/test/java/com/baeldung/test/dependencyinjection/PngFileEditorUnitTest.java
new file mode 100644
index 0000000000..d16f6d576e
--- /dev/null
+++ b/cdi/src/test/java/com/baeldung/test/dependencyinjection/PngFileEditorUnitTest.java
@@ -0,0 +1,39 @@
+package com.baeldung.test.dependencyinjection;
+
+import com.baeldung.dependencyinjection.imagefileeditors.PngFileEditor;
+import com.baeldung.dependencyinjection.qualifiers.PngFileEditorQualifier;
+import static org.assertj.core.api.Assertions.assertThat;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+@PngFileEditorQualifier
+public class PngFileEditorUnitTest {
+
+ private static PngFileEditor pngFileEditor;
+
+
+ @BeforeClass
+ public static void setPngFileEditorInstance() {
+ pngFileEditor = new PngFileEditor();
+ }
+
+ @Test
+ public void givenPngFileEditorInstance_whenCalledopenFile_thenOneAssertion() {
+ assertThat(pngFileEditor.openFile("file1.png")).isEqualTo("Opening PNG file file1.png");
+ }
+
+ @Test
+ public void givenPngFileEditorInstance_whenCallededitFile_thenOneAssertion() {
+ assertThat(pngFileEditor.editFile("file1.png")).isEqualTo("Editing PNG file file1.png");
+ }
+
+ @Test
+ public void givenPngFileEditorInstance_whenCalledwriteFile_thenOneAssertion() {
+ assertThat(pngFileEditor.writeFile("file1.png")).isEqualTo("Writing PNG file file1.png");
+ }
+
+ @Test
+ public void givenPngFileEditorInstance_whenCalledsaveFile_thenOneAssertion() {
+ assertThat(pngFileEditor.saveFile("file1.png")).isEqualTo("Saving PNG file file1.png");
+ }
+}
diff --git a/cdi/src/test/java/com/baeldung/test/dependencyinjection/TimeLoggerFactoryUnitTest.java b/cdi/src/test/java/com/baeldung/test/dependencyinjection/TimeLoggerFactoryUnitTest.java
new file mode 100644
index 0000000000..caf2ed32b5
--- /dev/null
+++ b/cdi/src/test/java/com/baeldung/test/dependencyinjection/TimeLoggerFactoryUnitTest.java
@@ -0,0 +1,15 @@
+package com.baeldung.test.dependencyinjection;
+
+import com.baeldung.dependencyinjection.factories.TimeLoggerFactory;
+import com.baeldung.dependencyinjection.loggers.TimeLogger;
+import static org.assertj.core.api.Assertions.assertThat;
+import org.junit.Test;
+
+public class TimeLoggerFactoryUnitTest {
+
+ @Test
+ public void givenTimeLoggerFactory_whenCalledgetTimeLogger_thenOneAssertion() {
+ TimeLoggerFactory timeLoggerFactory = new TimeLoggerFactory();
+ assertThat(timeLoggerFactory.getTimeLogger()).isInstanceOf(TimeLogger.class);
+ }
+}
diff --git a/cdi/src/test/java/com/baeldung/test/dependencyinjection/TimeLoggerUnitTest.java b/cdi/src/test/java/com/baeldung/test/dependencyinjection/TimeLoggerUnitTest.java
new file mode 100644
index 0000000000..222de251fe
--- /dev/null
+++ b/cdi/src/test/java/com/baeldung/test/dependencyinjection/TimeLoggerUnitTest.java
@@ -0,0 +1,20 @@
+package com.baeldung.test.dependencyinjection;
+
+import com.baeldung.dependencyinjection.loggers.TimeLogger;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import static org.assertj.core.api.Assertions.assertThat;
+import org.junit.Test;
+
+public class TimeLoggerUnitTest {
+
+
+ @Test
+ public void givenTimeLoggerInstance_whenCalledgetLogTime_thenOneAssertion() {
+ SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm");
+ Calendar calendar = Calendar.getInstance();
+ TimeLogger timeLogger = new TimeLogger(dateFormat, calendar);
+ String currentTime = dateFormat.format(calendar.getTime());
+ assertThat(timeLogger.getTime()).isEqualTo(currentTime);
+ }
+}
\ No newline at end of file
diff --git a/javax-servlets/src/main/java/com/baeldung/servlets/ErrorHandlerServlet.java b/javax-servlets/src/main/java/com/baeldung/servlets/ErrorHandlerServlet.java
new file mode 100644
index 0000000000..0008e837c0
--- /dev/null
+++ b/javax-servlets/src/main/java/com/baeldung/servlets/ErrorHandlerServlet.java
@@ -0,0 +1,34 @@
+package com.baeldung.servlets;
+
+import javax.servlet.annotation.*;
+import javax.servlet.http.*;
+import java.io.*;
+import java.util.*;
+
+import static javax.servlet.RequestDispatcher.*;
+
+@WebServlet(urlPatterns = "/errorHandler")
+public class ErrorHandlerServlet extends HttpServlet {
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws IOException {
+ resp.setContentType("text/html; charset=utf-8");
+ try (PrintWriter writer = resp.getWriter()) {
+ writer.write("Error description");
+ writer.write("Error description
");
+ writer.write("");
+ Arrays.asList(ERROR_STATUS_CODE, ERROR_EXCEPTION_TYPE, ERROR_MESSAGE)
+ .forEach(e ->
+ writer.write("- " + e + ":" + req.getAttribute(e) + "
")
+ );
+ writer.write("
");
+ writer.write("");
+ }
+
+ Exception exception = (Exception) req.getAttribute(ERROR_EXCEPTION);
+ if (IllegalArgumentException.class.isInstance(exception)) {
+ getServletContext().log("Error on an application argument", exception);
+ }
+ }
+}
\ No newline at end of file
diff --git a/javax-servlets/src/main/java/com/baeldung/servlets/RandomErrorServlet.java b/javax-servlets/src/main/java/com/baeldung/servlets/RandomErrorServlet.java
new file mode 100644
index 0000000000..c95bd2cec0
--- /dev/null
+++ b/javax-servlets/src/main/java/com/baeldung/servlets/RandomErrorServlet.java
@@ -0,0 +1,13 @@
+package com.baeldung.servlets;
+
+import javax.servlet.annotation.*;
+import javax.servlet.http.*;
+
+@WebServlet(urlPatterns = "/randomError")
+public class RandomErrorServlet extends HttpServlet {
+
+ @Override
+ protected void doGet(HttpServletRequest req, final HttpServletResponse resp) {
+ throw new IllegalStateException("Random error");
+ }
+}
\ No newline at end of file
diff --git a/javax-servlets/src/main/webapp/WEB-INF/web.xml b/javax-servlets/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000000..c9a06ac52d
--- /dev/null
+++ b/javax-servlets/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,16 @@
+
+
+
+ 404
+ /error-404.html
+
+
+
+ java.lang.Exception
+ /errorHandler
+
+
\ No newline at end of file
diff --git a/javax-servlets/src/main/webapp/error-404.html b/javax-servlets/src/main/webapp/error-404.html
new file mode 100644
index 0000000000..b36fc44160
--- /dev/null
+++ b/javax-servlets/src/main/webapp/error-404.html
@@ -0,0 +1,14 @@
+
+
+
+
+ Error page
+
+
+
+Error: Page not found
+
+Go back home
+
+
+
\ No newline at end of file
diff --git a/kotlin-ktor/.gitignore b/kotlin-ktor/.gitignore
new file mode 100644
index 0000000000..0c017e8f8c
--- /dev/null
+++ b/kotlin-ktor/.gitignore
@@ -0,0 +1,14 @@
+/bin/
+
+#ignore gradle
+.gradle/
+
+
+#ignore build and generated files
+build/
+node/
+out/
+
+#ignore installed node modules and package lock file
+node_modules/
+package-lock.json
diff --git a/kotlin-ktor/build.gradle b/kotlin-ktor/build.gradle
new file mode 100755
index 0000000000..11aef74857
--- /dev/null
+++ b/kotlin-ktor/build.gradle
@@ -0,0 +1,47 @@
+
+
+group 'com.baeldung.ktor'
+version '1.0-SNAPSHOT'
+
+
+buildscript {
+ ext.kotlin_version = '1.2.40'
+ ext.ktor_version = '0.9.2'
+
+ repositories {
+ mavenCentral()
+ }
+ dependencies {
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ }
+}
+
+apply plugin: 'java'
+apply plugin: 'kotlin'
+apply plugin: 'application'
+
+mainClassName = 'APIServer.kt'
+
+sourceCompatibility = 1.8
+compileKotlin { kotlinOptions.jvmTarget = "1.8" }
+compileTestKotlin { kotlinOptions.jvmTarget = "1.8" }
+
+kotlin { experimental { coroutines "enable" } }
+
+repositories {
+ mavenCentral()
+ jcenter()
+ maven { url "https://dl.bintray.com/kotlin/ktor" }
+}
+
+dependencies {
+ compile "io.ktor:ktor-server-netty:$ktor_version"
+ compile "ch.qos.logback:logback-classic:1.2.1"
+ compile "io.ktor:ktor-gson:$ktor_version"
+ testCompile group: 'junit', name: 'junit', version: '4.12'
+
+}
+task runServer(type: JavaExec) {
+ main = 'APIServer'
+ classpath = sourceSets.main.runtimeClasspath
+}
\ No newline at end of file
diff --git a/kotlin-ktor/gradle/wrapper/gradle-wrapper.jar b/kotlin-ktor/gradle/wrapper/gradle-wrapper.jar
new file mode 100755
index 0000000000..01b8bf6b1f
Binary files /dev/null and b/kotlin-ktor/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/kotlin-ktor/gradle/wrapper/gradle-wrapper.properties b/kotlin-ktor/gradle/wrapper/gradle-wrapper.properties
new file mode 100755
index 0000000000..0b83b5a3e3
--- /dev/null
+++ b/kotlin-ktor/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-bin.zip
diff --git a/kotlin-ktor/gradlew b/kotlin-ktor/gradlew
new file mode 100755
index 0000000000..cccdd3d517
--- /dev/null
+++ b/kotlin-ktor/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/kotlin-ktor/gradlew.bat b/kotlin-ktor/gradlew.bat
new file mode 100755
index 0000000000..e95643d6a2
--- /dev/null
+++ b/kotlin-ktor/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/kotlin-ktor/resources/logback.xml b/kotlin-ktor/resources/logback.xml
new file mode 100755
index 0000000000..274cdcdb02
--- /dev/null
+++ b/kotlin-ktor/resources/logback.xml
@@ -0,0 +1,11 @@
+
+
+
+ %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/kotlin-ktor/settings.gradle b/kotlin-ktor/settings.gradle
new file mode 100755
index 0000000000..13bbce9583
--- /dev/null
+++ b/kotlin-ktor/settings.gradle
@@ -0,0 +1,2 @@
+rootProject.name = 'KtorWithKotlin'
+
diff --git a/kotlin-ktor/src/main/kotlin/APIServer.kt b/kotlin-ktor/src/main/kotlin/APIServer.kt
new file mode 100755
index 0000000000..e67609e8b2
--- /dev/null
+++ b/kotlin-ktor/src/main/kotlin/APIServer.kt
@@ -0,0 +1,57 @@
+@file:JvmName("APIServer")
+
+
+
+import io.ktor.application.call
+import io.ktor.application.install
+import io.ktor.features.CallLogging
+import io.ktor.features.ContentNegotiation
+import io.ktor.features.DefaultHeaders
+import io.ktor.gson.gson
+import io.ktor.http.ContentType
+import io.ktor.request.path
+import io.ktor.response.respond
+import io.ktor.response.respondText
+import io.ktor.routing.get
+import io.ktor.routing.routing
+import io.ktor.server.engine.embeddedServer
+import io.ktor.server.netty.Netty
+import org.slf4j.event.Level
+
+data class Author(val name: String, val website: String)
+fun main(args: Array) {
+
+ val jsonResponse = """{
+ "id": 1,
+ "task": "Pay waterbill",
+ "description": "Pay water bill today",
+ }"""
+
+
+ embeddedServer(Netty, 8080) {
+ install(DefaultHeaders) {
+ header("X-Developer", "Baeldung")
+ }
+ install(CallLogging) {
+ level = Level.INFO
+ filter { call -> call.request.path().startsWith("/todo") }
+ filter { call -> call.request.path().startsWith("/author") }
+ }
+ install(ContentNegotiation) {
+ gson {
+ setPrettyPrinting()
+ }
+ }
+ routing {
+ get("/todo") {
+ call.respondText(jsonResponse, ContentType.Application.Json)
+ }
+ get("/author") {
+ val author = Author("baeldung", "baeldung.com")
+ call.respond(author)
+
+ }
+
+ }
+ }.start(wait = true)
+}
\ No newline at end of file
diff --git a/kotlin-ktor/webapp/WEB-INF/web.xml b/kotlin-ktor/webapp/WEB-INF/web.xml
new file mode 100755
index 0000000000..513a80cb27
--- /dev/null
+++ b/kotlin-ktor/webapp/WEB-INF/web.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+ io.ktor.ktor.config
+ application.conf
+
+
+
+ KtorServlet
+ KtorServlet
+ io.ktor.server.servlet.ServletApplicationEngine
+
+
+ true
+
+
+
+ 304857600
+ 304857600
+ 0
+
+
+
+
+ KtorServlet
+ /
+
+
+
\ No newline at end of file
diff --git a/libraries/.gitignore b/libraries/.gitignore
new file mode 100644
index 0000000000..ac45fafa62
--- /dev/null
+++ b/libraries/.gitignore
@@ -0,0 +1,8 @@
+*.class
+
+# Folders #
+/gensrc
+/target
+
+# Packaged files #
+*.jar
diff --git a/libraries/pom.xml b/libraries/pom.xml
index 663b9e68bb..e3a6656995 100644
--- a/libraries/pom.xml
+++ b/libraries/pom.xml
@@ -722,6 +722,17 @@
test
+
+ com.squareup
+ javapoet
+ ${javapoet.version}
+
+
+ org.hamcrest
+ hamcrest-all
+ ${hamcrest-all.version}
+ test
+
@@ -939,6 +950,8 @@
3.5.2
3.6
2.7.1
+ 1.10.0
+ 1.3
\ No newline at end of file
diff --git a/libraries/src/main/java/com/baeldung/javapoet/PersonGenerator.java b/libraries/src/main/java/com/baeldung/javapoet/PersonGenerator.java
new file mode 100644
index 0000000000..6dd41cc0bd
--- /dev/null
+++ b/libraries/src/main/java/com/baeldung/javapoet/PersonGenerator.java
@@ -0,0 +1,183 @@
+package com.baeldung.javapoet;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+
+import javax.lang.model.element.Modifier;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.IntStream;
+
+public class PersonGenerator {
+
+ private static final String FOUR_WHITESPACES = " ";
+ private static final String PERSON_PACKAGE_NAME = "com.baeldung.javapoet.test.person";
+
+ private File outputFile;
+
+ public PersonGenerator() {
+ outputFile = new File(getOutputPath().toUri());
+ }
+
+ public static String getPersonPackageName() {
+ return PERSON_PACKAGE_NAME;
+ }
+
+ public Path getOutputPath() {
+ return Paths.get(new File(".").getAbsolutePath() + "/gensrc");
+ }
+
+ public FieldSpec getDefaultNameField() {
+ return FieldSpec
+ .builder(String.class, "DEFAULT_NAME")
+ .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
+ .initializer("$S", "Alice")
+ .build();
+ }
+
+ public MethodSpec getSortByLengthMethod() {
+ return MethodSpec
+ .methodBuilder("sortByLength")
+ .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
+ .addParameter(ParameterSpec
+ .builder(ParameterizedTypeName.get(ClassName.get(List.class), TypeName.get(String.class)), "strings")
+ .build())
+ .addStatement("$T.sort($N, $L)", Collections.class, "strings", getComparatorAnonymousClass())
+ .build();
+ }
+
+ public MethodSpec getPrintNameMultipleTimesMethod() {
+ return MethodSpec
+ .methodBuilder("printNameMultipleTimes")
+ .addModifiers(Modifier.PUBLIC)
+ .addCode(getPrintNameMultipleTimesLambdaImpl())
+ .build();
+ }
+
+ public CodeBlock getPrintNameMultipleTimesImpl() {
+ return CodeBlock
+ .builder()
+ .beginControlFlow("for (int i = $L; i < $L; i++)")
+ .addStatement("System.out.println(name)")
+ .endControlFlow()
+ .build();
+ }
+
+ public CodeBlock getPrintNameMultipleTimesLambdaImpl() {
+ return CodeBlock
+ .builder()
+ .addStatement("$T<$T> names = new $T<>()", List.class, String.class, ArrayList.class)
+ .addStatement("$T.range($L, $L).forEach(i -> names.add(name))", IntStream.class, 0, 10)
+ .addStatement("names.forEach(System.out::println)")
+ .build();
+ }
+
+ public TypeSpec getGenderEnum() {
+ return TypeSpec
+ .enumBuilder("Gender")
+ .addModifiers(Modifier.PUBLIC)
+ .addEnumConstant("MALE")
+ .addEnumConstant("FEMALE")
+ .addEnumConstant("UNSPECIFIED")
+ .build();
+ }
+
+ public TypeSpec getPersonInterface() {
+ return TypeSpec
+ .interfaceBuilder("Person")
+ .addModifiers(Modifier.PUBLIC)
+ .addField(getDefaultNameField())
+ .addMethod(MethodSpec
+ .methodBuilder("getName")
+ .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
+ .returns(String.class)
+ .build())
+ .addMethod(MethodSpec
+ .methodBuilder("getDefaultName")
+ .addModifiers(Modifier.PUBLIC, Modifier.DEFAULT)
+ .returns(String.class)
+ .addCode(CodeBlock
+ .builder()
+ .addStatement("return DEFAULT_NAME")
+ .build())
+ .build())
+ .build();
+ }
+
+ public TypeSpec getStudentClass() {
+ return TypeSpec
+ .classBuilder("Student")
+ .addSuperinterface(ClassName.get(PERSON_PACKAGE_NAME, "Person"))
+ .addModifiers(Modifier.PUBLIC)
+ .addField(FieldSpec
+ .builder(String.class, "name")
+ .addModifiers(Modifier.PRIVATE)
+ .build())
+ .addMethod(MethodSpec
+ .methodBuilder("getName")
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PUBLIC)
+ .returns(String.class)
+ .addStatement("return this.name")
+ .build())
+ .addMethod(MethodSpec
+ .methodBuilder("setName")
+ .addParameter(String.class, "name")
+ .addModifiers(Modifier.PUBLIC)
+ .addStatement("this.name = name")
+ .build())
+ .addMethod(getPrintNameMultipleTimesMethod())
+ .addMethod(getSortByLengthMethod())
+ .build();
+ }
+
+ public TypeSpec getComparatorAnonymousClass() {
+ return TypeSpec
+ .anonymousClassBuilder("")
+ .addSuperinterface(ParameterizedTypeName.get(Comparator.class, String.class))
+ .addMethod(MethodSpec
+ .methodBuilder("compare")
+ .addModifiers(Modifier.PUBLIC)
+ .addAnnotation(Override.class)
+ .addParameter(String.class, "a")
+ .addParameter(String.class, "b")
+ .returns(int.class)
+ .addStatement("return a.length() - b.length()")
+ .build())
+ .build();
+ }
+
+ public void generateGenderEnum() throws IOException {
+ writeToOutputFile(getPersonPackageName(), getGenderEnum());
+ }
+
+ public void generatePersonInterface() throws IOException {
+ writeToOutputFile(getPersonPackageName(), getPersonInterface());
+ }
+
+ public void generateStudentClass() throws IOException {
+ writeToOutputFile(getPersonPackageName(), getStudentClass());
+ }
+
+ private void writeToOutputFile(String packageName, TypeSpec typeSpec) throws IOException {
+ JavaFile javaFile = JavaFile
+ .builder(packageName, typeSpec)
+ .indent(FOUR_WHITESPACES)
+ .build();
+ javaFile.writeTo(outputFile);
+ }
+
+}
diff --git a/libraries/src/test/java/com/baeldung/javapoet/test/PersonGeneratorUnitTest.java b/libraries/src/test/java/com/baeldung/javapoet/test/PersonGeneratorUnitTest.java
new file mode 100644
index 0000000000..61bf3ebc33
--- /dev/null
+++ b/libraries/src/test/java/com/baeldung/javapoet/test/PersonGeneratorUnitTest.java
@@ -0,0 +1,99 @@
+package com.baeldung.javapoet.test;
+
+import com.baeldung.javapoet.PersonGenerator;
+import org.apache.commons.io.FileUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+@RunWith(JUnit4.class)
+public class PersonGeneratorUnitTest {
+
+ private PersonGenerator generator;
+ private Path generatedFolderPath;
+ private Path expectedFolderPath;
+
+ @Before
+ public void setUp() {
+ String packagePath = this
+ .getClass()
+ .getPackage()
+ .getName()
+ .replace(".", "/") + "/person";
+ generator = new PersonGenerator();
+ generatedFolderPath = generator
+ .getOutputPath()
+ .resolve(packagePath);
+ expectedFolderPath = Paths.get(new File(".").getAbsolutePath() + "/src/test/java/" + packagePath);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ FileUtils.deleteDirectory(new File(generator
+ .getOutputPath()
+ .toUri()));
+ }
+
+ @Test
+ public void whenGenerateGenderEnum_thenGenerateGenderEnumAndWriteToFile() throws IOException {
+ generator.generateGenderEnum();
+ String fileName = "Gender.java";
+ assertThatFileIsGeneratedAsExpected(fileName);
+ deleteGeneratedFile(fileName);
+ }
+
+ @Test
+ public void whenGeneratePersonInterface_thenGeneratePersonInterfaceAndWriteToFile() throws IOException {
+ generator.generatePersonInterface();
+ String fileName = "Person.java";
+ assertThatFileIsGeneratedAsExpected(fileName);
+ deleteGeneratedFile(fileName);
+ }
+
+ @Test
+ public void whenGenerateStudentClass_thenGenerateStudentClassAndWriteToFile() throws IOException {
+ generator.generateStudentClass();
+ String fileName = "Student.java";
+ assertThatFileIsGeneratedAsExpected(fileName);
+ deleteGeneratedFile(fileName);
+ }
+
+ private void assertThatFileIsGeneratedAsExpected(String fileName) throws IOException {
+ String generatedFileContent = extractFileContent(generatedFolderPath.resolve(fileName));
+ String expectedFileContent = extractFileContent(expectedFolderPath.resolve(fileName));
+
+ assertThat("Generated file is identical to the file with the expected content", generatedFileContent, is(equalTo(expectedFileContent)));
+
+ }
+
+ private void deleteGeneratedFile(String fileName) throws IOException {
+ Path generatedFilePath = generatedFolderPath.resolve(fileName);
+ Files.delete(generatedFilePath);
+ }
+
+ private String extractFileContent(Path filePath) throws IOException {
+ byte[] fileContentAsBytes = Files.readAllBytes(filePath);
+ String fileContentAsString = new String(fileContentAsBytes, StandardCharsets.UTF_8);
+
+ if (!fileContentAsString.contains("\r\n")) {
+ // file is not in DOS format
+ // convert it first, so that the content comparison will be relevant
+ return fileContentAsString.replaceAll("\n", "\r\n");
+ }
+ return fileContentAsString;
+ }
+
+}
diff --git a/libraries/src/test/java/com/baeldung/javapoet/test/person/Gender.java b/libraries/src/test/java/com/baeldung/javapoet/test/person/Gender.java
new file mode 100644
index 0000000000..3c5657fb9d
--- /dev/null
+++ b/libraries/src/test/java/com/baeldung/javapoet/test/person/Gender.java
@@ -0,0 +1,9 @@
+package com.baeldung.javapoet.test.person;
+
+public enum Gender {
+ MALE,
+
+ FEMALE,
+
+ UNSPECIFIED
+}
diff --git a/libraries/src/test/java/com/baeldung/javapoet/test/person/Person.java b/libraries/src/test/java/com/baeldung/javapoet/test/person/Person.java
new file mode 100644
index 0000000000..fae8b23075
--- /dev/null
+++ b/libraries/src/test/java/com/baeldung/javapoet/test/person/Person.java
@@ -0,0 +1,13 @@
+package com.baeldung.javapoet.test.person;
+
+import java.lang.String;
+
+public interface Person {
+ String DEFAULT_NAME = "Alice";
+
+ String getName();
+
+ default String getDefaultName() {
+ return DEFAULT_NAME;
+ }
+}
diff --git a/libraries/src/test/java/com/baeldung/javapoet/test/person/Student.java b/libraries/src/test/java/com/baeldung/javapoet/test/person/Student.java
new file mode 100644
index 0000000000..1c7d5cc096
--- /dev/null
+++ b/libraries/src/test/java/com/baeldung/javapoet/test/person/Student.java
@@ -0,0 +1,37 @@
+package com.baeldung.javapoet.test.person;
+
+import java.lang.Override;
+import java.lang.String;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.IntStream;
+
+public class Student implements Person {
+ private String name;
+
+ @Override
+ public String getName() {
+ return this.name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void printNameMultipleTimes() {
+ List names = new ArrayList<>();
+ IntStream.range(0, 10).forEach(i -> names.add(name));
+ names.forEach(System.out::println);
+ }
+
+ public static void sortByLength(List strings) {
+ Collections.sort(strings, new Comparator() {
+ @Override
+ public int compare(String a, String b) {
+ return a.length() - b.length();
+ }
+ });
+ }
+}
diff --git a/pom.xml b/pom.xml
index 8c87983870..215da5d9d9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -248,6 +248,7 @@
persistence-modules/liquibase
spring-boot-property-exp
testing-modules/mockserver
+ testing-modules/test-containers
undertow
vertx-and-rxjava
saas
diff --git a/testing-modules/test-containers/README.md b/testing-modules/test-containers/README.md
new file mode 100644
index 0000000000..160893581d
--- /dev/null
+++ b/testing-modules/test-containers/README.md
@@ -0,0 +1,2 @@
+### Relevant Articles:
+- [Docker Test Containers in Java Tests](TODO link to be added.)
diff --git a/testing-modules/test-containers/pom.xml b/testing-modules/test-containers/pom.xml
new file mode 100644
index 0000000000..3551092c57
--- /dev/null
+++ b/testing-modules/test-containers/pom.xml
@@ -0,0 +1,119 @@
+
+
+ 4.0.0
+
+
+ test-containers
+ 1.0-SNAPSHOT
+
+ test-containers
+ Intro to Java Test Containers
+
+
+ com.baeldung
+ parent-modules
+ 1.0.0-SNAPSHOT
+ ../../
+
+
+
+
+
+
+ src/test/resources
+ true
+
+
+
+
+ maven-surefire-plugin
+ ${maven-surefire-plugin.version}
+
+
+ org.junit.platform
+ junit-platform-surefire-provider
+ ${junit.platform.version}
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 1.6.0
+
+
+
+ java
+
+
+
+
+ com.baeldung.TestLauncher
+
+
+
+
+
+
+
+ org.junit.platform
+ junit-platform-runner
+ ${junit.platform.version}
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+ ${junit.vintage.version}
+ test
+
+
+ org.apache.logging.log4j
+ log4j-core
+ ${log4j2.version}
+
+
+ org.testcontainers
+ testcontainers
+ 1.7.2
+
+
+ org.testcontainers
+ postgresql
+ 1.7.2
+
+
+ org.testcontainers
+ selenium
+ 1.7.2
+
+
+ org.postgresql
+ postgresql
+ 42.2.2
+
+
+ org.seleniumhq.selenium
+ selenium-remote-driver
+ 3.12.0
+
+
+
+
+
+ UTF-8
+ 1.8
+ 5.1.0
+ 1.0.1
+ 4.12.1
+ 2.8.2
+ 1.4.196
+ 2.11.0
+
+ 3.7.0
+ 2.19.1
+ 5.0.1.RELEASE
+
+
+
diff --git a/testing-modules/test-containers/src/test/java/com/baeldung/testconainers/DockerComposeContainerUnitTest.java b/testing-modules/test-containers/src/test/java/com/baeldung/testconainers/DockerComposeContainerUnitTest.java
new file mode 100644
index 0000000000..f51721ecde
--- /dev/null
+++ b/testing-modules/test-containers/src/test/java/com/baeldung/testconainers/DockerComposeContainerUnitTest.java
@@ -0,0 +1,47 @@
+package com.baeldung.testconainers;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.testcontainers.containers.DockerComposeContainer;
+
+public class DockerComposeContainerUnitTest {
+ @ClassRule
+ public static DockerComposeContainer compose =
+ new DockerComposeContainer(
+ new File("src/test/resources/test-compose.yml"))
+ .withExposedService("simpleWebServer_1", 80);
+
+ @Test
+ public void givenSimpleWebServerContainer_whenGetReuqest_thenReturnsResponse()
+ throws Exception {
+ String address = "http://" + compose.getServiceHost("simpleWebServer_1", 80)
+ + ":" + compose.getServicePort("simpleWebServer_1", 80);
+ String response = simpleGetRequest(address);
+
+ assertEquals(response, "Hello World!");
+ }
+
+ private String simpleGetRequest(String address) throws Exception {
+ URL url = new URL(address);
+ HttpURLConnection con = (HttpURLConnection) url.openConnection();
+ con.setRequestMethod("GET");
+
+ BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
+ String inputLine;
+ StringBuffer content = new StringBuffer();
+ while ((inputLine = in.readLine()) != null) {
+ content.append(inputLine);
+ }
+ in.close();
+
+ return content.toString();
+ }
+}
diff --git a/testing-modules/test-containers/src/test/java/com/baeldung/testconainers/GenericContainerUnitTest.java b/testing-modules/test-containers/src/test/java/com/baeldung/testconainers/GenericContainerUnitTest.java
new file mode 100644
index 0000000000..32dbba7dcf
--- /dev/null
+++ b/testing-modules/test-containers/src/test/java/com/baeldung/testconainers/GenericContainerUnitTest.java
@@ -0,0 +1,51 @@
+package com.baeldung.testconainers;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.platform.commons.annotation.Testable;
+import org.testcontainers.containers.GenericContainer;
+
+@Testable
+public class GenericContainerUnitTest {
+ @ClassRule
+ public static GenericContainer simpleWebServer =
+ new GenericContainer("alpine:3.2")
+ .withExposedPorts(80)
+ .withCommand("/bin/sh", "-c", "while true; do echo "
+ + "\"HTTP/1.1 200 OK\n\nHello World!\" | nc -l -p 80; done");
+
+ @Test
+ public void givenSimpleWebServerContainer_whenGetReuqest_thenReturnsResponse()
+ throws Exception {
+ String address = "http://"
+ + simpleWebServer.getContainerIpAddress()
+ + ":" + simpleWebServer.getMappedPort(80);
+ String response = simpleGetRequest(address);
+
+ assertEquals(response, "Hello World!");
+ }
+
+ private String simpleGetRequest(String address) throws Exception {
+ URL url = new URL(address);
+ HttpURLConnection con = (HttpURLConnection) url.openConnection();
+ con.setRequestMethod("GET");
+
+ BufferedReader in = new BufferedReader(
+ new InputStreamReader(con.getInputStream()));
+ String inputLine;
+ StringBuffer content = new StringBuffer();
+ while ((inputLine = in.readLine()) != null) {
+ content.append(inputLine);
+ }
+ in.close();
+
+ return content.toString();
+ }
+}
diff --git a/testing-modules/test-containers/src/test/java/com/baeldung/testconainers/PostgreSqlContainerUnitTest.java b/testing-modules/test-containers/src/test/java/com/baeldung/testconainers/PostgreSqlContainerUnitTest.java
new file mode 100644
index 0000000000..f458f1a999
--- /dev/null
+++ b/testing-modules/test-containers/src/test/java/com/baeldung/testconainers/PostgreSqlContainerUnitTest.java
@@ -0,0 +1,36 @@
+package com.baeldung.testconainers;
+
+import static org.junit.Assert.assertEquals;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.platform.commons.annotation.Testable;
+import org.testcontainers.containers.PostgreSQLContainer;
+
+@Testable
+public class PostgreSqlContainerUnitTest {
+ @Rule
+ public PostgreSQLContainer postgresContainer = new PostgreSQLContainer();
+
+ @Test
+ public void whenSelectQueryExecuted_thenResulstsReturned() throws Exception {
+ ResultSet resultSet = performQuery(postgresContainer, "SELECT 1");
+ resultSet.next();
+ int result = resultSet.getInt(1);
+ assertEquals(1, result);
+ }
+
+ private ResultSet performQuery(PostgreSQLContainer postgres, String query) throws SQLException {
+ String jdbcUrl = postgres.getJdbcUrl();
+ String username = postgres.getUsername();
+ String password = postgres.getPassword();
+ Connection conn = DriverManager.getConnection(jdbcUrl, username, password);
+ return conn.createStatement()
+ .executeQuery(query);
+ }
+}
diff --git a/testing-modules/test-containers/src/test/java/com/baeldung/testconainers/WebDriverContainerUnitTest.java b/testing-modules/test-containers/src/test/java/com/baeldung/testconainers/WebDriverContainerUnitTest.java
new file mode 100644
index 0000000000..c10deac0f7
--- /dev/null
+++ b/testing-modules/test-containers/src/test/java/com/baeldung/testconainers/WebDriverContainerUnitTest.java
@@ -0,0 +1,30 @@
+package com.baeldung.testconainers;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.openqa.selenium.By;
+import org.openqa.selenium.remote.DesiredCapabilities;
+import org.openqa.selenium.remote.RemoteWebDriver;
+import org.testcontainers.DockerClientFactory;
+import org.testcontainers.containers.BrowserWebDriverContainer;
+import org.testcontainers.containers.GenericContainer;
+
+public class WebDriverContainerUnitTest {
+ @Rule
+ public BrowserWebDriverContainer chrome
+ = new BrowserWebDriverContainer()
+ .withDesiredCapabilities(DesiredCapabilities.chrome());
+
+ @Test
+ public void whenNavigatedToPage_thenHeadingIsInThePage() {
+ RemoteWebDriver driver = chrome.getWebDriver();
+ driver.get("https://saucelabs.com/test/guinea-pig");
+ String heading = driver.findElement(By.xpath("/html/body/h1"))
+ .getText();
+ assertEquals("This page is a Selenium sandbox", heading);
+ }
+
+}
diff --git a/testing-modules/test-containers/src/test/resources/test-compose.yml b/testing-modules/test-containers/src/test/resources/test-compose.yml
new file mode 100644
index 0000000000..3810c1c589
--- /dev/null
+++ b/testing-modules/test-containers/src/test/resources/test-compose.yml
@@ -0,0 +1,3 @@
+simpleWebServer:
+ image: alpine:3.2
+ command: ["/bin/sh", "-c", "while true; do echo 'HTTP/1.1 200 OK\n\nHello World!' | nc -l -p 80; done"]