+ * ({@link Constants#INERTIA_FACTOR} * {@link Particle#getSpeed()}) +
+ * (({@link Constants#COGNITIVE_WEIGHT} * random(0,1)) * ({@link Particle#getBestPosition()} - {@link Particle#getPosition()})) +
+ * (({@link Constants#SOCIAL_WEIGHT} * random(0,1)) * ({@link Swarm#getBestPosition()} - {@link Particle#getPosition()})) +
+ * (({@link Constants#GLOBAL_WEIGHT} * random(0,1)) * ({@link #bestPosition} - {@link Particle#getPosition()}))
+ *
+ *
+ * @param particle
+ * the particle whose new speed needs to be computed
+ * @param swarm
+ * the swarm which contains the particle
+ * @param index
+ * the index of the particle axis whose speeds needs to be
+ * computed
+ * @return the new speed of the particle passed on the given axis
+ */
+ private int getNewParticleSpeedForIndex(Particle particle, Swarm swarm, int index) {
+ return (int) ((Constants.INERTIA_FACTOR * particle.getSpeed()[index])
+ + (randomizePercentage(Constants.COGNITIVE_WEIGHT)
+ * (particle.getBestPosition()[index] - particle.getPosition()[index]))
+ + (randomizePercentage(Constants.SOCIAL_WEIGHT)
+ * (swarm.getBestPosition()[index] - particle.getPosition()[index]))
+ + (randomizePercentage(Constants.GLOBAL_WEIGHT)
+ * (bestPosition[index] - particle.getPosition()[index])));
+ }
+
+ /**
+ * Returns a random number between 0 and the value passed as argument.
+ *
+ * @param value
+ * the value to randomize
+ * @return a random value between 0 and the one passed as argument
+ */
+ private double randomizePercentage(double value) {
+ return random.nextDouble() * value;
+ }
+
+ /**
+ * Gets the {@link #bestPosition}.
+ *
+ * @return the {@link #bestPosition}
+ */
+ public long[] getBestPosition() {
+ return bestPosition;
+ }
+
+ /**
+ * Gets the {@link #bestFitness}.
+ *
+ * @return the {@link #bestFitness}
+ */
+ public double getBestFitness() {
+ return bestFitness;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ long temp;
+ temp = Double.doubleToLongBits(bestFitness);
+ result = prime * result + (int) (temp ^ (temp >>> 32));
+ result = prime * result + Arrays.hashCode(bestPosition);
+ result = prime * result + ((fitnessFunction == null) ? 0 : fitnessFunction.hashCode());
+ result = prime * result + ((random == null) ? 0 : random.hashCode());
+ result = prime * result + Arrays.hashCode(swarms);
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Multiswarm other = (Multiswarm) obj;
+ if (Double.doubleToLongBits(bestFitness) != Double.doubleToLongBits(other.bestFitness))
+ return false;
+ if (!Arrays.equals(bestPosition, other.bestPosition))
+ return false;
+ if (fitnessFunction == null) {
+ if (other.fitnessFunction != null)
+ return false;
+ } else if (!fitnessFunction.equals(other.fitnessFunction))
+ return false;
+ if (random == null) {
+ if (other.random != null)
+ return false;
+ } else if (!random.equals(other.random))
+ return false;
+ if (!Arrays.equals(swarms, other.swarms))
+ return false;
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "Multiswarm [swarms=" + Arrays.toString(swarms) + ", bestPosition=" + Arrays.toString(bestPosition)
+ + ", bestFitness=" + bestFitness + ", random=" + random + ", fitnessFunction=" + fitnessFunction + "]";
+ }
+
+}
diff --git a/algorithms/src/main/java/com/baeldung/algorithms/multiswarm/Particle.java b/algorithms/src/main/java/com/baeldung/algorithms/multiswarm/Particle.java
new file mode 100644
index 0000000000..5930a94267
--- /dev/null
+++ b/algorithms/src/main/java/com/baeldung/algorithms/multiswarm/Particle.java
@@ -0,0 +1,204 @@
+package com.baeldung.algorithms.multiswarm;
+
+import java.util.Arrays;
+
+/**
+ * Represents a particle, the basic component of a {@link Swarm}.
+ *
+ * @author Donato Rimenti
+ *
+ */
+public class Particle {
+
+ /**
+ * The current position of this particle.
+ */
+ private long[] position;
+
+ /**
+ * The speed of this particle.
+ */
+ private long[] speed;
+
+ /**
+ * The fitness of this particle for the current position.
+ */
+ private double fitness;
+
+ /**
+ * The best position found by this particle.
+ */
+ private long[] bestPosition;
+
+ /**
+ * The best fitness found by this particle.
+ */
+ private double bestFitness = Double.NEGATIVE_INFINITY;
+
+ /**
+ * Instantiates a new Particle.
+ *
+ * @param initialPosition
+ * the initial {@link #position}
+ * @param initialSpeed
+ * the initial {@link #speed}
+ */
+ public Particle(long[] initialPosition, long[] initialSpeed) {
+ this.position = initialPosition;
+ this.speed = initialSpeed;
+ }
+
+ /**
+ * Gets the {@link #position}.
+ *
+ * @return the {@link #position}
+ */
+ public long[] getPosition() {
+ return position;
+ }
+
+ /**
+ * Gets the {@link #speed}.
+ *
+ * @return the {@link #speed}
+ */
+ public long[] getSpeed() {
+ return speed;
+ }
+
+ /**
+ * Gets the {@link #fitness}.
+ *
+ * @return the {@link #fitness}
+ */
+ public double getFitness() {
+ return fitness;
+ }
+
+ /**
+ * Gets the {@link #bestPosition}.
+ *
+ * @return the {@link #bestPosition}
+ */
+ public long[] getBestPosition() {
+ return bestPosition;
+ }
+
+ /**
+ * Gets the {@link #bestFitness}.
+ *
+ * @return the {@link #bestFitness}
+ */
+ public double getBestFitness() {
+ return bestFitness;
+ }
+
+ /**
+ * Sets the {@link #position}.
+ *
+ * @param position
+ * the new {@link #position}
+ */
+ public void setPosition(long[] position) {
+ this.position = position;
+ }
+
+ /**
+ * Sets the {@link #speed}.
+ *
+ * @param speed
+ * the new {@link #speed}
+ */
+ public void setSpeed(long[] speed) {
+ this.speed = speed;
+ }
+
+ /**
+ * Sets the {@link #fitness}.
+ *
+ * @param fitness
+ * the new {@link #fitness}
+ */
+ public void setFitness(double fitness) {
+ this.fitness = fitness;
+ }
+
+ /**
+ * Sets the {@link #bestPosition}.
+ *
+ * @param bestPosition
+ * the new {@link #bestPosition}
+ */
+ public void setBestPosition(long[] bestPosition) {
+ this.bestPosition = bestPosition;
+ }
+
+ /**
+ * Sets the {@link #bestFitness}.
+ *
+ * @param bestFitness
+ * the new {@link #bestFitness}
+ */
+ public void setBestFitness(double bestFitness) {
+ this.bestFitness = bestFitness;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ long temp;
+ temp = Double.doubleToLongBits(bestFitness);
+ result = prime * result + (int) (temp ^ (temp >>> 32));
+ result = prime * result + Arrays.hashCode(bestPosition);
+ temp = Double.doubleToLongBits(fitness);
+ result = prime * result + (int) (temp ^ (temp >>> 32));
+ result = prime * result + Arrays.hashCode(position);
+ result = prime * result + Arrays.hashCode(speed);
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Particle other = (Particle) obj;
+ if (Double.doubleToLongBits(bestFitness) != Double.doubleToLongBits(other.bestFitness))
+ return false;
+ if (!Arrays.equals(bestPosition, other.bestPosition))
+ return false;
+ if (Double.doubleToLongBits(fitness) != Double.doubleToLongBits(other.fitness))
+ return false;
+ if (!Arrays.equals(position, other.position))
+ return false;
+ if (!Arrays.equals(speed, other.speed))
+ return false;
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "Particle [position=" + Arrays.toString(position) + ", speed=" + Arrays.toString(speed) + ", fitness="
+ + fitness + ", bestPosition=" + Arrays.toString(bestPosition) + ", bestFitness=" + bestFitness + "]";
+ }
+
+}
diff --git a/algorithms/src/main/java/com/baeldung/algorithms/multiswarm/Swarm.java b/algorithms/src/main/java/com/baeldung/algorithms/multiswarm/Swarm.java
new file mode 100644
index 0000000000..e6d37bb7e6
--- /dev/null
+++ b/algorithms/src/main/java/com/baeldung/algorithms/multiswarm/Swarm.java
@@ -0,0 +1,155 @@
+package com.baeldung.algorithms.multiswarm;
+
+import java.util.Arrays;
+import java.util.Random;
+
+/**
+ * Represents a collection of {@link Particle}.
+ *
+ * @author Donato Rimenti
+ *
+ */
+public class Swarm {
+
+ /**
+ * The particles of this swarm.
+ */
+ private Particle[] particles;
+
+ /**
+ * The best position found within the particles of this swarm.
+ */
+ private long[] bestPosition;
+
+ /**
+ * The best fitness score found within the particles of this swarm.
+ */
+ private double bestFitness = Double.NEGATIVE_INFINITY;
+
+ /**
+ * A random generator.
+ */
+ private Random random = new Random();
+
+ /**
+ * Instantiates a new Swarm.
+ *
+ * @param numParticles
+ * the number of particles of the swarm
+ */
+ public Swarm(int numParticles) {
+ particles = new Particle[numParticles];
+ for (int i = 0; i < numParticles; i++) {
+ long[] initialParticlePosition = { random.nextInt(Constants.PARTICLE_UPPER_BOUND),
+ random.nextInt(Constants.PARTICLE_UPPER_BOUND) };
+ long[] initialParticleSpeed = { random.nextInt(Constants.PARTICLE_UPPER_BOUND),
+ random.nextInt(Constants.PARTICLE_UPPER_BOUND) };
+ particles[i] = new Particle(initialParticlePosition, initialParticleSpeed);
+ }
+ }
+
+ /**
+ * Gets the {@link #particles}.
+ *
+ * @return the {@link #particles}
+ */
+ public Particle[] getParticles() {
+ return particles;
+ }
+
+ /**
+ * Gets the {@link #bestPosition}.
+ *
+ * @return the {@link #bestPosition}
+ */
+ public long[] getBestPosition() {
+ return bestPosition;
+ }
+
+ /**
+ * Gets the {@link #bestFitness}.
+ *
+ * @return the {@link #bestFitness}
+ */
+ public double getBestFitness() {
+ return bestFitness;
+ }
+
+ /**
+ * Sets the {@link #bestPosition}.
+ *
+ * @param bestPosition
+ * the new {@link #bestPosition}
+ */
+ public void setBestPosition(long[] bestPosition) {
+ this.bestPosition = bestPosition;
+ }
+
+ /**
+ * Sets the {@link #bestFitness}.
+ *
+ * @param bestFitness
+ * the new {@link #bestFitness}
+ */
+ public void setBestFitness(double bestFitness) {
+ this.bestFitness = bestFitness;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ long temp;
+ temp = Double.doubleToLongBits(bestFitness);
+ result = prime * result + (int) (temp ^ (temp >>> 32));
+ result = prime * result + Arrays.hashCode(bestPosition);
+ result = prime * result + Arrays.hashCode(particles);
+ result = prime * result + ((random == null) ? 0 : random.hashCode());
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Swarm other = (Swarm) obj;
+ if (Double.doubleToLongBits(bestFitness) != Double.doubleToLongBits(other.bestFitness))
+ return false;
+ if (!Arrays.equals(bestPosition, other.bestPosition))
+ return false;
+ if (!Arrays.equals(particles, other.particles))
+ return false;
+ if (random == null) {
+ if (other.random != null)
+ return false;
+ } else if (!random.equals(other.random))
+ return false;
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "Swarm [particles=" + Arrays.toString(particles) + ", bestPosition=" + Arrays.toString(bestPosition)
+ + ", bestFitness=" + bestFitness + ", random=" + random + "]";
+ }
+
+}
diff --git a/algorithms/src/main/java/com/baeldung/algorithms/numberwordconverter/NumberWordConverter.java b/algorithms/src/main/java/com/baeldung/algorithms/numberwordconverter/NumberWordConverter.java
new file mode 100644
index 0000000000..0fe2960f96
--- /dev/null
+++ b/algorithms/src/main/java/com/baeldung/algorithms/numberwordconverter/NumberWordConverter.java
@@ -0,0 +1,75 @@
+package com.baeldung.algorithms.numberwordconverter;
+
+import java.math.BigDecimal;
+
+import pl.allegro.finance.tradukisto.MoneyConverters;
+
+public class NumberWordConverter {
+
+ public static final String INVALID_INPUT_GIVEN = "Invalid input given";
+
+ public static final String[] ones = { "", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" };
+
+ public static final String[] tens = {
+ "", // 0
+ "", // 1
+ "twenty", // 2
+ "thirty", // 3
+ "forty", // 4
+ "fifty", // 5
+ "sixty", // 6
+ "seventy", // 7
+ "eighty", // 8
+ "ninety" // 9
+ };
+
+ public static String getMoneyIntoWords(String input) {
+ MoneyConverters converter = MoneyConverters.ENGLISH_BANKING_MONEY_VALUE;
+ return converter.asWords(new BigDecimal(input));
+ }
+
+ public static String getMoneyIntoWords(final double money) {
+ long dollar = (long) money;
+ long cents = Math.round((money - dollar) * 100);
+ if (money == 0D) {
+ return "";
+ }
+ if (money < 0) {
+ return INVALID_INPUT_GIVEN;
+ }
+ String dollarPart = "";
+ if (dollar > 0) {
+ dollarPart = convert(dollar) + " dollar" + (dollar == 1 ? "" : "s");
+ }
+ String centsPart = "";
+ if (cents > 0) {
+ if (dollarPart.length() > 0) {
+ centsPart = " and ";
+ }
+ centsPart += convert(cents) + " cent" + (cents == 1 ? "" : "s");
+ }
+ return dollarPart + centsPart;
+ }
+
+ private static String convert(final long n) {
+ if (n < 0) {
+ return INVALID_INPUT_GIVEN;
+ }
+ if (n < 20) {
+ return ones[(int) n];
+ }
+ if (n < 100) {
+ return tens[(int) n / 10] + ((n % 10 != 0) ? " " : "") + ones[(int) n % 10];
+ }
+ if (n < 1000) {
+ return ones[(int) n / 100] + " hundred" + ((n % 100 != 0) ? " " : "") + convert(n % 100);
+ }
+ if (n < 1_000_000) {
+ return convert(n / 1000) + " thousand" + ((n % 1000 != 0) ? " " : "") + convert(n % 1000);
+ }
+ if (n < 1_000_000_000) {
+ return convert(n / 1_000_000) + " million" + ((n % 1_000_000 != 0) ? " " : "") + convert(n % 1_000_000);
+ }
+ return convert(n / 1_000_000_000) + " billion" + ((n % 1_000_000_000 != 0) ? " " : "") + convert(n % 1_000_000_000);
+ }
+}
\ No newline at end of file
diff --git a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/BacktrackingAlgorithm.java b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/BacktrackingAlgorithm.java
new file mode 100644
index 0000000000..4b37558aab
--- /dev/null
+++ b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/BacktrackingAlgorithm.java
@@ -0,0 +1,104 @@
+package com.baeldung.algorithms.sudoku;
+
+import java.util.stream.IntStream;
+
+public class BacktrackingAlgorithm {
+
+ private static final int BOARD_SIZE = 9;
+ private static final int SUBSECTION_SIZE = 3;
+ private static final int BOARD_START_INDEX = 0;
+
+ private static final int NO_VALUE = 0;
+ private static final int MIN_VALUE = 1;
+ private static final int MAX_VALUE = 9;
+
+ private static int[][] board = {
+ {8, 0, 0, 0, 0, 0, 0, 0, 0},
+ {0, 0, 3, 6, 0, 0, 0, 0, 0},
+ {0, 7, 0, 0, 9, 0, 2, 0, 0},
+ {0, 5, 0, 0, 0, 7, 0, 0, 0},
+ {0, 0, 0, 0, 4, 5, 7, 0, 0},
+ {0, 0, 0, 1, 0, 0, 0, 3, 0},
+ {0, 0, 1, 0, 0, 0, 0, 6, 8},
+ {0, 0, 8, 5, 0, 0, 0, 1, 0},
+ {0, 9, 0, 0, 0, 0, 4, 0, 0}
+ };
+
+ public static void main(String[] args) {
+ BacktrackingAlgorithm solver = new BacktrackingAlgorithm();
+ solver.solve(board);
+ solver.printBoard();
+ }
+
+ private void printBoard() {
+ for (int row = BOARD_START_INDEX; row < BOARD_SIZE; row++) {
+ for (int column = BOARD_START_INDEX; column < BOARD_SIZE; column++) {
+ System.out.print(board[row][column] + " ");
+ }
+ System.out.println();
+ }
+ }
+
+ private boolean solve(int[][] board) {
+ for (int row = BOARD_START_INDEX; row < BOARD_SIZE; row++) {
+ for (int column = BOARD_START_INDEX; column < BOARD_SIZE; column++) {
+ if (board[row][column] == NO_VALUE) {
+ for (int k = MIN_VALUE; k <= MAX_VALUE; k++) {
+ board[row][column] = k;
+ if (isValid(board, row, column) && solve(board)) {
+ return true;
+ }
+ board[row][column] = NO_VALUE;
+ }
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private boolean isValid(int[][] board, int row, int column) {
+ return rowConstraint(board, row) &&
+ columnConstraint(board, column) &&
+ subsectionConstraint(board, row, column);
+ }
+
+ private boolean subsectionConstraint(int[][] board, int row, int column) {
+ boolean[] constraint = new boolean[BOARD_SIZE];
+ int subsectionRowStart = (row / SUBSECTION_SIZE) * SUBSECTION_SIZE;
+ int subsectionRowEnd = subsectionRowStart + SUBSECTION_SIZE;
+
+ int subsectionColumnStart = (column / SUBSECTION_SIZE) * SUBSECTION_SIZE;
+ int subsectionColumnEnd = subsectionColumnStart + SUBSECTION_SIZE;
+
+ for (int r = subsectionRowStart; r < subsectionRowEnd; r++) {
+ for (int c = subsectionColumnStart; c < subsectionColumnEnd; c++) {
+ if (!checkConstraint(board, r, constraint, c)) return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean columnConstraint(int[][] board, int column) {
+ boolean[] constraint = new boolean[BOARD_SIZE];
+ return IntStream.range(BOARD_START_INDEX, BOARD_SIZE)
+ .allMatch(row -> checkConstraint(board, row, constraint, column));
+ }
+
+ private boolean rowConstraint(int[][] board, int row) {
+ boolean[] constraint = new boolean[BOARD_SIZE];
+ return IntStream.range(BOARD_START_INDEX, BOARD_SIZE)
+ .allMatch(column -> checkConstraint(board, row, constraint, column));
+ }
+
+ private boolean checkConstraint(int[][] board, int row, boolean[] constraint, int column) {
+ if (board[row][column] != NO_VALUE) {
+ if (!constraint[board[row][column] - 1]) {
+ constraint[board[row][column] - 1] = true;
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/ColumnNode.java b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/ColumnNode.java
new file mode 100644
index 0000000000..46995ca42f
--- /dev/null
+++ b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/ColumnNode.java
@@ -0,0 +1,33 @@
+package com.baeldung.algorithms.sudoku;
+
+class ColumnNode extends DancingNode {
+ int size;
+ String name;
+
+ ColumnNode(String n) {
+ super();
+ size = 0;
+ name = n;
+ C = this;
+ }
+
+ void cover() {
+ unlinkLR();
+ for (DancingNode i = this.D; i != this; i = i.D) {
+ for (DancingNode j = i.R; j != i; j = j.R) {
+ j.unlinkUD();
+ j.C.size--;
+ }
+ }
+ }
+
+ void uncover() {
+ for (DancingNode i = this.U; i != this; i = i.U) {
+ for (DancingNode j = i.L; j != i; j = j.L) {
+ j.C.size++;
+ j.relinkUD();
+ }
+ }
+ relinkLR();
+ }
+}
\ No newline at end of file
diff --git a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinks.java b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinks.java
new file mode 100644
index 0000000000..d3cbb2bd02
--- /dev/null
+++ b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinks.java
@@ -0,0 +1,133 @@
+package com.baeldung.algorithms.sudoku;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+public class DancingLinks {
+
+ private ColumnNode header;
+ private List| Title | +Author | +Words # | +Date Published | +
| ${article.title} | +${article.author} | +${article.words} | +${article.date} | +
| Title | +Author | +Words # | +Date Published | +
| ${article.title} | +${article.author} | +${article.words} | +${article.date} | +
getQuotes(String baseCurrency, LocalDate date); +} diff --git a/java-spi/exchange-rate-api/src/main/java/com/baeldung/rate/exception/ProviderNotFoundException.java b/java-spi/exchange-rate-api/src/main/java/com/baeldung/rate/exception/ProviderNotFoundException.java new file mode 100644 index 0000000000..3a2d92c4fd --- /dev/null +++ b/java-spi/exchange-rate-api/src/main/java/com/baeldung/rate/exception/ProviderNotFoundException.java @@ -0,0 +1,13 @@ +package com.baeldung.rate.exception; + +public class ProviderNotFoundException extends RuntimeException { + + public ProviderNotFoundException() { + super(); + } + + public ProviderNotFoundException(String message) { + super(message); + } + +} diff --git a/java-spi/exchange-rate-api/src/main/java/com/baeldung/rate/spi/ExchangeRateProvider.java b/java-spi/exchange-rate-api/src/main/java/com/baeldung/rate/spi/ExchangeRateProvider.java new file mode 100644 index 0000000000..c3157b2b03 --- /dev/null +++ b/java-spi/exchange-rate-api/src/main/java/com/baeldung/rate/spi/ExchangeRateProvider.java @@ -0,0 +1,7 @@ +package com.baeldung.rate.spi; + +import com.baeldung.rate.api.QuoteManager; + +public interface ExchangeRateProvider { + QuoteManager create(); +} \ No newline at end of file diff --git a/java-spi/exchange-rate-app/pom.xml b/java-spi/exchange-rate-app/pom.xml new file mode 100644 index 0000000000..7e64cf7438 --- /dev/null +++ b/java-spi/exchange-rate-app/pom.xml @@ -0,0 +1,27 @@ ++ diff --git a/java-spi/exchange-rate-app/src/main/java/com.baeldung.rate.app/MainApp.java b/java-spi/exchange-rate-app/src/main/java/com.baeldung.rate.app/MainApp.java new file mode 100644 index 0000000000..fd43ed3a85 --- /dev/null +++ b/java-spi/exchange-rate-app/src/main/java/com.baeldung.rate.app/MainApp.java @@ -0,0 +1,21 @@ +package com.baeldung.rate.app; + +import com.baeldung.rate.ExchangeRate; +import com.baeldung.rate.api.Quote; + +import java.time.LocalDate; +import java.util.List; + +public class MainApp { + public static void main(String... args) { + ExchangeRate.providers().forEach(provider -> { + System.out.println("Retreiving USD quotes from provider :" + provider); + List4.0.0 + +exchange-rate-app +jar + ++ + +com.baeldung +java-spi +1.0.0-SNAPSHOT ++ + ++ +com.baeldung +exchange-rate-api +1.0.0-SNAPSHOT ++ +com.baeldung +exchange-rate-impl +1.0.0-SNAPSHOT +quotes = provider.create().getQuotes("USD", LocalDate.now()); + System.out.println(String.format("%14s%12s|%12s", "","Ask", "Bid")); + System.out.println("----------------------------------------"); + quotes.forEach(quote -> { + System.out.println("USD --> " + quote.getCurrency() + " : " + String.format("%12f|%12f", quote.getAsk(), quote.getBid())); + }); + }); + } +} diff --git a/java-spi/exchange-rate-impl/pom.xml b/java-spi/exchange-rate-impl/pom.xml new file mode 100644 index 0000000000..ec22791351 --- /dev/null +++ b/java-spi/exchange-rate-impl/pom.xml @@ -0,0 +1,42 @@ ++ diff --git a/java-spi/exchange-rate-impl/src/main/java/com/baeldung/rate/impl/QuoteResponse.java b/java-spi/exchange-rate-impl/src/main/java/com/baeldung/rate/impl/QuoteResponse.java new file mode 100644 index 0000000000..9ba4fb26b0 --- /dev/null +++ b/java-spi/exchange-rate-impl/src/main/java/com/baeldung/rate/impl/QuoteResponse.java @@ -0,0 +1,26 @@ +package com.baeldung.rate.impl; + +import com.baeldung.rate.api.Quote; + +import java.util.List; + +public class QuoteResponse { + private List4.0.0 + +exchange-rate-impl +jar + ++ + +com.baeldung +java-spi +1.0.0-SNAPSHOT ++ + ++ +com.baeldung +exchange-rate-api +1.0.0-SNAPSHOT ++ +com.squareup.okhttp3 +okhttp +3.10.0 ++ +javax.json.bind +javax.json.bind-api +1.0 ++ +org.eclipse +yasson +1.0.1 ++ +org.glassfish +javax.json +1.1.2 +result; + private String error; + + public ListgetResult() { + return result; + } + + public void setResult(Listresult) { + this.result = result; + } + + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } +} diff --git a/java-spi/exchange-rate-impl/src/main/java/com/baeldung/rate/impl/QuoteResponseWrapper.java b/java-spi/exchange-rate-impl/src/main/java/com/baeldung/rate/impl/QuoteResponseWrapper.java new file mode 100644 index 0000000000..6d7be086f0 --- /dev/null +++ b/java-spi/exchange-rate-impl/src/main/java/com/baeldung/rate/impl/QuoteResponseWrapper.java @@ -0,0 +1,13 @@ +package com.baeldung.rate.impl; + +public class QuoteResponseWrapper { + private QuoteResponse quoteResponse; + + public QuoteResponse getQuoteResponse() { + return quoteResponse; + } + + public void setQuoteResponse(QuoteResponse quoteResponse) { + this.quoteResponse = quoteResponse; + } +} diff --git a/java-spi/exchange-rate-impl/src/main/java/com/baeldung/rate/impl/YahooFinanceExchangeRateProvider.java b/java-spi/exchange-rate-impl/src/main/java/com/baeldung/rate/impl/YahooFinanceExchangeRateProvider.java new file mode 100644 index 0000000000..9a069cfde4 --- /dev/null +++ b/java-spi/exchange-rate-impl/src/main/java/com/baeldung/rate/impl/YahooFinanceExchangeRateProvider.java @@ -0,0 +1,13 @@ +package com.baeldung.rate.impl; + +import com.baeldung.rate.api.QuoteManager; +import com.baeldung.rate.spi.ExchangeRateProvider; + +public class YahooFinanceExchangeRateProvider implements ExchangeRateProvider { + + @Override + public QuoteManager create() { + return new YahooQuoteManagerImpl(); + } + +} \ No newline at end of file diff --git a/java-spi/exchange-rate-impl/src/main/java/com/baeldung/rate/impl/YahooQuoteManagerImpl.java b/java-spi/exchange-rate-impl/src/main/java/com/baeldung/rate/impl/YahooQuoteManagerImpl.java new file mode 100644 index 0000000000..8cc68259be --- /dev/null +++ b/java-spi/exchange-rate-impl/src/main/java/com/baeldung/rate/impl/YahooQuoteManagerImpl.java @@ -0,0 +1,65 @@ +package com.baeldung.rate.impl; + +import com.baeldung.rate.api.Quote; +import com.baeldung.rate.api.QuoteManager; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +import javax.json.bind.JsonbBuilder; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.time.LocalDate; +import java.util.Currency; +import java.util.List; + +public class YahooQuoteManagerImpl implements QuoteManager { + + static final String URL_PROVIDER = "https://query1.finance.yahoo.com/v7/finance/quote"; + OkHttpClient client = new OkHttpClient(); + + @Override + public ListgetQuotes(String baseCurrency, LocalDate date) { + + StringBuilder sb = new StringBuilder(); + Currency.getAvailableCurrencies().forEach(currency -> { + if (!currency.equals(currency.getCurrencyCode())) { + sb.append(baseCurrency).append(currency.getCurrencyCode()).append("=X").append(","); + } + }); + + String value = ""; + try { + value = URLEncoder.encode(sb.toString().substring(0, sb.toString().length() - 1), "UTF-8"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + String queryString = String.format("%s=%s", "symbols", value); + String response = doGetRequest(queryString); + System.out.println(response); + return map(response); + } + + private Listmap(String response) { + QuoteResponseWrapper qrw = JsonbBuilder.create().fromJson(response, QuoteResponseWrapper.class); + return qrw.getQuoteResponse().getResult(); + } + + String doGetRequest(String queryString) { + String fullUrl = URL_PROVIDER + "?" + queryString; + + System.out.println(fullUrl); + Request request = new Request.Builder() + .url(fullUrl) + .build(); + Response response = null; + try { + response = client.newCall(request).execute(); + return response.body().string(); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/java-spi/exchange-rate-impl/src/main/resources/META-INF/services/com.baeldung.rate.spi.ExchangeRateProvider b/java-spi/exchange-rate-impl/src/main/resources/META-INF/services/com.baeldung.rate.spi.ExchangeRateProvider new file mode 100644 index 0000000000..67ba8a8227 --- /dev/null +++ b/java-spi/exchange-rate-impl/src/main/resources/META-INF/services/com.baeldung.rate.spi.ExchangeRateProvider @@ -0,0 +1 @@ +com.baeldung.rate.impl.YahooFinanceExchangeRateProvider \ No newline at end of file diff --git a/java-spi/pom.xml b/java-spi/pom.xml new file mode 100644 index 0000000000..8eaa184d8d --- /dev/null +++ b/java-spi/pom.xml @@ -0,0 +1,20 @@ ++ diff --git a/javax-servlets/src/main/java/com/baeldung/controller/StudentServlet.java b/javax-servlets/src/main/java/com/baeldung/controller/StudentServlet.java new file mode 100644 index 0000000000..b1924198a4 --- /dev/null +++ b/javax-servlets/src/main/java/com/baeldung/controller/StudentServlet.java @@ -0,0 +1,39 @@ +package com.baeldung.controller; + +import com.baeldung.service.StudentService; + +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@WebServlet(name = "StudentServlet", urlPatterns = "/student-record") +public class StudentServlet extends HttpServlet { + + private final StudentService studentService = new StudentService(); + + private void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String studentID = request.getParameter("id"); + if (studentID != null) { + int id = Integer.parseInt(studentID); + studentService.getStudent(id) + .ifPresent(s -> request.setAttribute("studentRecord", s)); + } + + RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/jsp/student-record.jsp"); + dispatcher.forward(request, response); + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + processRequest(request, response); + } + + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + processRequest(request, response); + } +} diff --git a/javax-servlets/src/main/java/com/baeldung/model/Student.java b/javax-servlets/src/main/java/com/baeldung/model/Student.java new file mode 100644 index 0000000000..ce8a27375a --- /dev/null +++ b/javax-servlets/src/main/java/com/baeldung/model/Student.java @@ -0,0 +1,39 @@ +package com.baeldung.model; + +public class Student { + + private int id; + private String firstName; + private String lastName; + + public Student(int id, String firstName, String lastName) { + super(); + this.id = id; + this.firstName = firstName; + this.lastName = lastName; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } +} diff --git a/javax-servlets/src/main/java/com/baeldung/service/StudentService.java b/javax-servlets/src/main/java/com/baeldung/service/StudentService.java new file mode 100644 index 0000000000..525d47683f --- /dev/null +++ b/javax-servlets/src/main/java/com/baeldung/service/StudentService.java @@ -0,0 +1,21 @@ +package com.baeldung.service; + +import com.baeldung.model.Student; + +import java.util.Optional; + +public class StudentService { + + public Optional4.0.0 + +java-spi +pom + ++ + +com.baeldung +parent-modules +1.0.0-SNAPSHOT ++ + +exchange-rate-api +exchange-rate-impl +exchange-rate-app +getStudent(int id) { + switch (id) { + case 1: + return Optional.of(new Student(1, "John", "Doe")); + case 2: + return Optional.of(new Student(2, "Jane", "Goodall")); + case 3: + return Optional.of(new Student(3, "Max", "Born")); + default: + return Optional.empty(); + } + } +} diff --git a/javax-servlets/src/main/java/com/baeldung/servlets/FormServlet.java b/javax-servlets/src/main/java/com/baeldung/servlets/FormServlet.java index fcd9143dff..c78129a9cf 100644 --- a/javax-servlets/src/main/java/com/baeldung/servlets/FormServlet.java +++ b/javax-servlets/src/main/java/com/baeldung/servlets/FormServlet.java @@ -1,7 +1,6 @@ package com.baeldung.servlets; import javax.servlet.RequestDispatcher; -import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; @@ -13,7 +12,7 @@ public class FormServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { + throws IOException { String height = request.getParameter("height"); String weight = request.getParameter("weight"); @@ -25,23 +24,20 @@ public class FormServlet extends HttpServlet { response.setHeader("Test", "Success"); response.setHeader("BMI", String.valueOf(bmi)); - RequestDispatcher dispatcher = request.getRequestDispatcher("index.jsp"); + RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/jsp/index.jsp"); dispatcher.forward(request, response); } catch (Exception e) { - response.sendRedirect("index.jsp"); } } @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { + protected void doGet(HttpServletRequest request, HttpServletResponse response) { // do something else here } private Double calculateBMI(Double weight, Double height) { - return weight / (height * height); } } \ No newline at end of file diff --git a/javax-servlets/web/index.jsp b/javax-servlets/src/main/webapp/WEB-INF/jsp/index.jsp similarity index 100% rename from javax-servlets/web/index.jsp rename to javax-servlets/src/main/webapp/WEB-INF/jsp/index.jsp diff --git a/javax-servlets/src/main/webapp/WEB-INF/jsp/student-record.jsp b/javax-servlets/src/main/webapp/WEB-INF/jsp/student-record.jsp new file mode 100644 index 0000000000..4b9d9b378f --- /dev/null +++ b/javax-servlets/src/main/webapp/WEB-INF/jsp/student-record.jsp @@ -0,0 +1,36 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> + +<%@ page import="com.baeldung.model.Student"%> + + + + + Student Record + + + <% + if (request.getAttribute("studentRecord") != null) { + Student student = (Student) request.getAttribute("studentRecord"); + %> + +Student Record
++ ID: + <%=student.getId()%>++ First Name: + <%=student.getFirstName()%>++ Last Name: + <%=student.getLastName()%>+ + <% + } else { + %> +No student record found.
+ <% + } + %> + + \ No newline at end of file diff --git a/javax-servlets/web/WEB-INF/web.xml b/javax-servlets/src/main/webapp/WEB-INF/web.xml similarity index 100% rename from javax-servlets/web/WEB-INF/web.xml rename to javax-servlets/src/main/webapp/WEB-INF/web.xml diff --git a/javaxval/pom.xml b/javaxval/pom.xml index 6a83a25f01..a05ee43aaf 100644 --- a/javaxval/pom.xml +++ b/javaxval/pom.xml @@ -6,10 +6,11 @@0.1-SNAPSHOT - @@ -21,12 +22,6 @@2.0.0.Final -6.0.2.Final +2.0.1.Final +6.0.7.Final 3.0.0 2.2.6 +5.0.2.RELEASE - diff --git a/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/MethodValidationConfig.java b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/MethodValidationConfig.java new file mode 100644 index 0000000000..206a145337 --- /dev/null +++ b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/MethodValidationConfig.java @@ -0,0 +1,38 @@ +package org.baeldung.javaxval.methodvalidation; + +import org.baeldung.javaxval.methodvalidation.model.Customer; +import org.baeldung.javaxval.methodvalidation.model.Reservation; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Scope; +import org.springframework.validation.beanvalidation.MethodValidationPostProcessor; + +import java.time.LocalDate; + +@Configuration +@ComponentScan({ "org.baeldung.javaxval.methodvalidation.model" }) +public class MethodValidationConfig { + + @Bean + public MethodValidationPostProcessor methodValidationPostProcessor() { + return new MethodValidationPostProcessor(); + } + + @Bean("customer") + @Scope(BeanDefinition.SCOPE_PROTOTYPE) + public Customer customer(String firstName, String lastName) { + + Customer customer = new Customer(firstName, lastName); + return customer; + } + + @Bean("reservation") + @Scope(BeanDefinition.SCOPE_PROTOTYPE) + public Reservation reservation(LocalDate begin, LocalDate end, Customer customer, int room) { + + Reservation reservation = new Reservation(begin, end, customer, room); + return reservation; + } +} diff --git a/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ConsistentDateParameterValidator.java b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ConsistentDateParameterValidator.java new file mode 100644 index 0000000000..f1c97760d7 --- /dev/null +++ b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ConsistentDateParameterValidator.java @@ -0,0 +1,25 @@ +package org.baeldung.javaxval.methodvalidation.constraints; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import javax.validation.constraintvalidation.SupportedValidationTarget; +import javax.validation.constraintvalidation.ValidationTarget; +import java.time.LocalDate; + +@SupportedValidationTarget(ValidationTarget.PARAMETERS) +public class ConsistentDateParameterValidator implements ConstraintValidator- -javax.validation -validation-api -${validation-api.version} -+ org.hibernate hibernate-validator @@ -50,6 +45,16 @@javax.el ${javax.el.version} + +org.springframework +spring-context +${org.springframework.version} ++ org.springframework +spring-test +${org.springframework.version} +{ + + @Override + public boolean isValid(Object[] value, ConstraintValidatorContext context) { + + if (value[0] == null || value[1] == null) { + return true; + } + + if (!(value[0] instanceof LocalDate) || !(value[1] instanceof LocalDate)) { + throw new IllegalArgumentException("Illegal method signature, expected two parameters of type LocalDate."); + } + + return ((LocalDate) value[0]).isAfter(LocalDate.now()) && ((LocalDate) value[0]).isBefore((LocalDate) value[1]); + } +} diff --git a/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ConsistentDateParameters.java b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ConsistentDateParameters.java new file mode 100644 index 0000000000..6b321f545c --- /dev/null +++ b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ConsistentDateParameters.java @@ -0,0 +1,23 @@ +package org.baeldung.javaxval.methodvalidation.constraints; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Constraint(validatedBy = ConsistentDateParameterValidator.class) +@Target({ METHOD, CONSTRUCTOR }) +@Retention(RUNTIME) +@Documented +public @interface ConsistentDateParameters { + + String message() default "End date must be after begin date and both must be in the future"; + + Class>[] groups() default {}; + + Class extends Payload>[] payload() default {}; +} diff --git a/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ValidReservation.java b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ValidReservation.java new file mode 100644 index 0000000000..f9cdea1483 --- /dev/null +++ b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ValidReservation.java @@ -0,0 +1,24 @@ +package org.baeldung.javaxval.methodvalidation.constraints; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Constraint(validatedBy = ValidReservationValidator.class) +@Target({ METHOD, CONSTRUCTOR }) +@Retention(RUNTIME) +@Documented +public @interface ValidReservation { + + String message() default "End date must be after begin date and both must be in the future, room number must be bigger than 0"; + + Class>[] groups() default {}; + + Class extends Payload>[] payload() default {}; +} diff --git a/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ValidReservationValidator.java b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ValidReservationValidator.java new file mode 100644 index 0000000000..7b730480ed --- /dev/null +++ b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/constraints/ValidReservationValidator.java @@ -0,0 +1,32 @@ +package org.baeldung.javaxval.methodvalidation.constraints; + +import org.baeldung.javaxval.methodvalidation.model.Reservation; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import java.time.LocalDate; + +public class ValidReservationValidator implements ConstraintValidator { + + @Override + public boolean isValid(Reservation reservation, ConstraintValidatorContext context) { + + if (reservation == null) { + return true; + } + + if (!(reservation instanceof Reservation)) { + throw new IllegalArgumentException("Illegal method signature, expected parameter of type Reservation."); + } + + if (reservation.getBegin() == null || reservation.getEnd() == null || reservation.getCustomer() == null) { + return false; + } + + return (reservation.getBegin() + .isAfter(LocalDate.now()) + && reservation.getBegin() + .isBefore(reservation.getEnd()) + && reservation.getRoom() > 0); + } +} diff --git a/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/model/Customer.java b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/model/Customer.java new file mode 100644 index 0000000000..fe9ad7080e --- /dev/null +++ b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/model/Customer.java @@ -0,0 +1,41 @@ +package org.baeldung.javaxval.methodvalidation.model; + +import org.springframework.validation.annotation.Validated; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +@Validated +public class Customer { + + @Size(min = 5, max = 200) + private String firstName; + + @Size(min = 5, max = 200) + private String lastName; + + public Customer(@Size(min = 5, max = 200) @NotNull String firstName, @Size(min = 5, max = 200) @NotNull String lastName) { + this.firstName = firstName; + this.lastName = lastName; + } + + public Customer() { + + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } +} diff --git a/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/model/Reservation.java b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/model/Reservation.java new file mode 100644 index 0000000000..a8c01d2be1 --- /dev/null +++ b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/model/Reservation.java @@ -0,0 +1,64 @@ +package org.baeldung.javaxval.methodvalidation.model; + +import org.baeldung.javaxval.methodvalidation.constraints.ConsistentDateParameters; +import org.baeldung.javaxval.methodvalidation.constraints.ValidReservation; +import org.springframework.validation.annotation.Validated; + +import javax.validation.Valid; +import javax.validation.constraints.Positive; +import java.time.LocalDate; + +@Validated +public class Reservation { + + private LocalDate begin; + + private LocalDate end; + + @Valid + private Customer customer; + + @Positive + private int room; + + @ConsistentDateParameters + @ValidReservation + public Reservation(LocalDate begin, LocalDate end, Customer customer, int room) { + this.begin = begin; + this.end = end; + this.customer = customer; + this.room = room; + } + + public LocalDate getBegin() { + return begin; + } + + public void setBegin(LocalDate begin) { + this.begin = begin; + } + + public LocalDate getEnd() { + return end; + } + + public void setEnd(LocalDate end) { + this.end = end; + } + + public Customer getCustomer() { + return customer; + } + + public void setCustomer(Customer customer) { + this.customer = customer; + } + + public int getRoom() { + return room; + } + + public void setRoom(int room) { + this.room = room; + } +} diff --git a/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/model/ReservationManagement.java b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/model/ReservationManagement.java new file mode 100644 index 0000000000..f6fec1a15d --- /dev/null +++ b/javaxval/src/main/java/org/baeldung/javaxval/methodvalidation/model/ReservationManagement.java @@ -0,0 +1,50 @@ +package org.baeldung.javaxval.methodvalidation.model; + +import org.baeldung.javaxval.methodvalidation.constraints.ConsistentDateParameters; +import org.baeldung.javaxval.methodvalidation.constraints.ValidReservation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Controller; +import org.springframework.validation.annotation.Validated; + +import javax.validation.Valid; +import javax.validation.constraints.*; +import java.time.LocalDate; +import java.util.List; + +@Controller +@Validated +public class ReservationManagement { + + @Autowired + private ApplicationContext applicationContext; + + @ConsistentDateParameters + public void createReservation(LocalDate begin, LocalDate end, @NotNull Customer customer) { + + // ... + } + + public void createReservation(@NotNull @Future LocalDate begin, @Min(1) int duration, @NotNull Customer customer) { + + // ... + } + + public void createReservation(@Valid Reservation reservation) { + + // ... + } + + @NotNull + @Size(min = 1) + public List<@NotNull Customer> getAllCustomers() { + + return null; + } + + @Valid + public Reservation getReservationById(int id) { + + return null; + } +} diff --git a/javaxval/src/test/java/org/baeldung/javaxval/methodvalidation/ContainerValidationIntegrationTest.java b/javaxval/src/test/java/org/baeldung/javaxval/methodvalidation/ContainerValidationIntegrationTest.java new file mode 100644 index 0000000000..2363bf8f5d --- /dev/null +++ b/javaxval/src/test/java/org/baeldung/javaxval/methodvalidation/ContainerValidationIntegrationTest.java @@ -0,0 +1,97 @@ +package org.baeldung.javaxval.methodvalidation; + +import org.baeldung.javaxval.methodvalidation.model.Customer; +import org.baeldung.javaxval.methodvalidation.model.Reservation; +import org.baeldung.javaxval.methodvalidation.model.ReservationManagement; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.AnnotationConfigContextLoader; + +import javax.validation.ConstraintViolationException; +import java.time.LocalDate; +import java.util.List; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = { MethodValidationConfig.class }, loader = AnnotationConfigContextLoader.class) +public class ContainerValidationIntegrationTest { + + @Autowired + ReservationManagement reservationManagement; + + @Rule + public final ExpectedException exception = ExpectedException.none(); + + @Test + public void whenValidationWithInvalidMethodParameters_thenConstraintViolationException() { + + exception.expect(ConstraintViolationException.class); + reservationManagement.createReservation(LocalDate.now(), 0, null); + } + + @Test + public void whenValidationWithValidMethodParameters_thenNoException() { + + reservationManagement.createReservation(LocalDate.now() + .plusDays(1), 1, new Customer("William", "Smith")); + } + + @Test + public void whenCrossParameterValidationWithInvalidParameters_thenConstraintViolationException() { + + exception.expect(ConstraintViolationException.class); + reservationManagement.createReservation(LocalDate.now(), LocalDate.now(), null); + } + + @Test + public void whenCrossParameterValidationWithValidParameters_thenNoException() { + + reservationManagement.createReservation(LocalDate.now() + .plusDays(1), + LocalDate.now() + .plusDays(2), + new Customer("William", "Smith")); + } + + @Test + public void whenValidationWithInvalidReturnValue_thenConstraintViolationException() { + + exception.expect(ConstraintViolationException.class); + List list = reservationManagement.getAllCustomers(); + } + + @Test + public void whenValidationWithInvalidCascadedValue_thenConstraintViolationException() { + + Customer customer = new Customer(); + customer.setFirstName("John"); + customer.setLastName("Doe"); + Reservation reservation = new Reservation(LocalDate.now() + .plusDays(1), + LocalDate.now() + .plusDays(2), + customer, 1); + + exception.expect(ConstraintViolationException.class); + reservationManagement.createReservation(reservation); + } + + @Test + public void whenValidationWithValidCascadedValue_thenCNoException() { + + Customer customer = new Customer(); + customer.setFirstName("William"); + customer.setLastName("Smith"); + Reservation reservation = new Reservation(LocalDate.now() + .plusDays(1), + LocalDate.now() + .plusDays(2), + customer, 1); + + reservationManagement.createReservation(reservation); + } +} diff --git a/javaxval/src/test/java/org/baeldung/javaxval/methodvalidation/ValidationIntegrationTest.java b/javaxval/src/test/java/org/baeldung/javaxval/methodvalidation/ValidationIntegrationTest.java new file mode 100644 index 0000000000..6b53d3a107 --- /dev/null +++ b/javaxval/src/test/java/org/baeldung/javaxval/methodvalidation/ValidationIntegrationTest.java @@ -0,0 +1,209 @@ +package org.baeldung.javaxval.methodvalidation; + +import org.baeldung.javaxval.methodvalidation.model.Customer; +import org.baeldung.javaxval.methodvalidation.model.Reservation; +import org.baeldung.javaxval.methodvalidation.model.ReservationManagement; +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.*; + +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.ValidatorFactory; +import javax.validation.executable.ExecutableValidator; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.time.LocalDate; +import java.util.Collections; +import java.util.Set; + +public class ValidationIntegrationTest { + + private ExecutableValidator executableValidator; + + @Before + public void getExecutableValidator() { + + ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); + this.executableValidator = factory.getValidator() + .forExecutables(); + } + + @Test + public void whenValidationWithInvalidMethodParameters_thenCorrectNumberOfVoilations() throws NoSuchMethodException { + + ReservationManagement object = new ReservationManagement(); + Method method = ReservationManagement.class.getMethod("createReservation", LocalDate.class, int.class, Customer.class); + Object[] parameterValues = { LocalDate.now(), 0, null }; + Set > violations = executableValidator.validateParameters(object, method, parameterValues); + + assertEquals(3, violations.size()); + } + + @Test + public void whenValidationWithValidMethodParameters_thenZeroVoilations() throws NoSuchMethodException { + + ReservationManagement object = new ReservationManagement(); + Method method = ReservationManagement.class.getMethod("createReservation", LocalDate.class, int.class, Customer.class); + Object[] parameterValues = { LocalDate.now() + .plusDays(1), 1, new Customer("John", "Doe") }; + Set > violations = executableValidator.validateParameters(object, method, parameterValues); + + assertEquals(0, violations.size()); + } + + @Test + public void whenCrossParameterValidationWithInvalidParameters_thenCorrectNumberOfVoilations() throws NoSuchMethodException { + + ReservationManagement object = new ReservationManagement(); + Method method = ReservationManagement.class.getMethod("createReservation", LocalDate.class, LocalDate.class, Customer.class); + Object[] parameterValues = { LocalDate.now(), LocalDate.now(), new Customer("John", "Doe") }; + Set > violations = executableValidator.validateParameters(object, method, parameterValues); + + assertEquals(1, violations.size()); + } + + @Test + public void whenCrossParameterValidationWithValidParameters_thenZeroVoilations() throws NoSuchMethodException { + + ReservationManagement object = new ReservationManagement(); + Method method = ReservationManagement.class.getMethod("createReservation", LocalDate.class, LocalDate.class, Customer.class); + Object[] parameterValues = { LocalDate.now() + .plusDays(1), + LocalDate.now() + .plusDays(2), + new Customer("John", "Doe") }; + Set > violations = executableValidator.validateParameters(object, method, parameterValues); + + assertEquals(0, violations.size()); + } + + @Test + public void whenValidationWithInvalidConstructorParameters_thenCorrectNumberOfVoilations() throws NoSuchMethodException { + + Constructor constructor = Customer.class.getConstructor(String.class, String.class); + Object[] parameterValues = { "John", "Doe" }; + Set > violations = executableValidator.validateConstructorParameters(constructor, parameterValues); + + assertEquals(2, violations.size()); + } + + @Test + public void whenValidationWithValidConstructorParameters_thenZeroVoilations() throws NoSuchMethodException { + + Constructor constructor = Customer.class.getConstructor(String.class, String.class); + Object[] parameterValues = { "William", "Smith" }; + Set > violations = executableValidator.validateConstructorParameters(constructor, parameterValues); + + assertEquals(0, violations.size()); + } + + @Test + public void whenCrossParameterValidationWithInvalidConstructorParameters_thenCorrectNumberOfVoilations() throws NoSuchMethodException { + + Constructor constructor = Reservation.class.getConstructor(LocalDate.class, LocalDate.class, Customer.class, int.class); + Object[] parameterValues = { LocalDate.now(), LocalDate.now(), new Customer("William", "Smith"), 1 }; + Set > violations = executableValidator.validateConstructorParameters(constructor, parameterValues); + + assertEquals(1, violations.size()); + } + + @Test + public void whenCrossParameterValidationWithValidConstructorParameters_thenZeroVoilations() throws NoSuchMethodException { + + Constructor constructor = Reservation.class.getConstructor(LocalDate.class, LocalDate.class, Customer.class, int.class); + Object[] parameterValues = { LocalDate.now() + .plusDays(1), + LocalDate.now() + .plusDays(2), + new Customer("William", "Smith"), 1 }; + Set > violations = executableValidator.validateConstructorParameters(constructor, parameterValues); + + assertEquals(0, violations.size()); + } + + @Test + public void whenValidationWithInvalidReturnValue_thenCorrectNumberOfVoilations() throws NoSuchMethodException { + + ReservationManagement object = new ReservationManagement(); + Method method = ReservationManagement.class.getMethod("getAllCustomers"); + Object returnValue = Collections. emptyList(); + Set > violations = executableValidator.validateReturnValue(object, method, returnValue); + + assertEquals(1, violations.size()); + } + + @Test + public void whenValidationWithValidReturnValue_thenZeroVoilations() throws NoSuchMethodException { + + ReservationManagement object = new ReservationManagement(); + Method method = ReservationManagement.class.getMethod("getAllCustomers"); + Object returnValue = Collections.singletonList(new Customer("William", "Smith")); + Set > violations = executableValidator.validateReturnValue(object, method, returnValue); + + assertEquals(0, violations.size()); + } + + @Test + public void whenValidationWithInvalidConstructorReturnValue_thenCorrectNumberOfVoilations() throws NoSuchMethodException { + + Constructor constructor = Reservation.class.getConstructor(LocalDate.class, LocalDate.class, Customer.class, int.class); + Reservation createdObject = new Reservation(LocalDate.now(), LocalDate.now(), new Customer("William", "Smith"), 0); + Set > violations = executableValidator.validateConstructorReturnValue(constructor, createdObject); + + assertEquals(1, violations.size()); + } + + @Test + public void whenValidationWithValidConstructorReturnValue_thenZeroVoilations() throws NoSuchMethodException { + + Constructor constructor = Reservation.class.getConstructor(LocalDate.class, LocalDate.class, Customer.class, int.class); + Reservation createdObject = new Reservation(LocalDate.now() + .plusDays(1), + LocalDate.now() + .plusDays(2), + new Customer("William", "Smith"), 1); + Set > violations = executableValidator.validateConstructorReturnValue(constructor, createdObject); + + assertEquals(0, violations.size()); + } + + @Test + public void whenValidationWithInvalidCascadedValue_thenCorrectNumberOfVoilations() throws NoSuchMethodException { + + ReservationManagement object = new ReservationManagement(); + Method method = ReservationManagement.class.getMethod("createReservation", Reservation.class); + Customer customer = new Customer(); + customer.setFirstName("John"); + customer.setLastName("Doe"); + Reservation reservation = new Reservation(LocalDate.now() + .plusDays(1), + LocalDate.now() + .plusDays(2), + customer, 1); + Object[] parameterValues = { reservation }; + Set > violations = executableValidator.validateParameters(object, method, parameterValues); + + assertEquals(2, violations.size()); + } + + @Test + public void whenValidationWithValidCascadedValue_thenCorrectNumberOfVoilations() throws NoSuchMethodException { + + ReservationManagement object = new ReservationManagement(); + Method method = ReservationManagement.class.getMethod("createReservation", Reservation.class); + Customer customer = new Customer(); + customer.setFirstName("William"); + customer.setLastName("Smith"); + Reservation reservation = new Reservation(LocalDate.now() + .plusDays(1), + LocalDate.now() + .plusDays(2), + customer, 1); + Object[] parameterValues = { reservation }; + Set > violations = executableValidator.validateParameters(object, method, parameterValues); + + assertEquals(0, violations.size()); + } + +} diff --git a/jenkins/README.md b/jenkins/README.md new file mode 100644 index 0000000000..da60e556df --- /dev/null +++ b/jenkins/README.md @@ -0,0 +1,3 @@ +## Relevant articles: + +- [Writing a Jenkins Plugin](http://www.baeldung.com/jenkins-custom-plugin) diff --git a/jersey/pom.xml b/jersey/pom.xml new file mode 100644 index 0000000000..0c8b6b9281 --- /dev/null +++ b/jersey/pom.xml @@ -0,0 +1,63 @@ + + + diff --git a/jersey/src/main/java/com/baeldung/jersey/client/JerseyClient.java b/jersey/src/main/java/com/baeldung/jersey/client/JerseyClient.java new file mode 100644 index 0000000000..88ad891411 --- /dev/null +++ b/jersey/src/main/java/com/baeldung/jersey/client/JerseyClient.java @@ -0,0 +1,45 @@ +package com.baeldung.jersey.client; + +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.Entity; +import javax.ws.rs.core.Response; + +import org.glassfish.jersey.client.ClientConfig; + +import com.baeldung.jersey.client.filter.RequestClientFilter; +import com.baeldung.jersey.client.filter.ResponseClientFilter; +import com.baeldung.jersey.client.interceptor.RequestClientWriterInterceptor; + +public class JerseyClient { + + private static final String URI_GREETINGS = "http://localhost:8080/jersey/greetings"; + + public static String getHelloGreeting() { + return createClient().target(URI_GREETINGS) + .request() + .get(String.class); + } + + public static String getHiGreeting() { + return createClient().target(URI_GREETINGS + "/hi") + .request() + .get(String.class); + } + + public static Response getCustomGreeting() { + return createClient().target(URI_GREETINGS + "/custom") + .request() + .post(Entity.text("custom")); + } + + private static Client createClient() { + ClientConfig config = new ClientConfig(); + config.register(RequestClientFilter.class); + config.register(ResponseClientFilter.class); + config.register(RequestClientWriterInterceptor.class); + + return ClientBuilder.newClient(config); + } + +} diff --git a/jersey/src/main/java/com/baeldung/jersey/client/filter/RequestClientFilter.java b/jersey/src/main/java/com/baeldung/jersey/client/filter/RequestClientFilter.java new file mode 100644 index 0000000000..8c6ac2c5fb --- /dev/null +++ b/jersey/src/main/java/com/baeldung/jersey/client/filter/RequestClientFilter.java @@ -0,0 +1,24 @@ +package com.baeldung.jersey.client.filter; + +import java.io.IOException; + +import javax.ws.rs.client.ClientRequestContext; +import javax.ws.rs.client.ClientRequestFilter; +import javax.ws.rs.ext.Provider; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Provider +public class RequestClientFilter implements ClientRequestFilter { + + private static final Logger LOG = LoggerFactory.getLogger(RequestClientFilter.class); + + @Override + public void filter(ClientRequestContext requestContext) throws IOException { + LOG.info("Request client filter"); + + requestContext.setProperty("test", "test client request filter"); + } + +} diff --git a/jersey/src/main/java/com/baeldung/jersey/client/filter/ResponseClientFilter.java b/jersey/src/main/java/com/baeldung/jersey/client/filter/ResponseClientFilter.java new file mode 100644 index 0000000000..1676fa2094 --- /dev/null +++ b/jersey/src/main/java/com/baeldung/jersey/client/filter/ResponseClientFilter.java @@ -0,0 +1,26 @@ +package com.baeldung.jersey.client.filter; + +import java.io.IOException; + +import javax.ws.rs.client.ClientRequestContext; +import javax.ws.rs.client.ClientResponseContext; +import javax.ws.rs.client.ClientResponseFilter; +import javax.ws.rs.ext.Provider; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Provider +public class ResponseClientFilter implements ClientResponseFilter { + + private static final Logger LOG = LoggerFactory.getLogger(ResponseClientFilter.class); + + @Override + public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException { + LOG.info("Response client filter"); + + responseContext.getHeaders() + .add("X-Test-Client", "Test response client filter"); + } + +} diff --git a/jersey/src/main/java/com/baeldung/jersey/client/interceptor/RequestClientWriterInterceptor.java b/jersey/src/main/java/com/baeldung/jersey/client/interceptor/RequestClientWriterInterceptor.java new file mode 100644 index 0000000000..7216cf18cc --- /dev/null +++ b/jersey/src/main/java/com/baeldung/jersey/client/interceptor/RequestClientWriterInterceptor.java @@ -0,0 +1,28 @@ +package com.baeldung.jersey.client.interceptor; + +import java.io.IOException; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.ext.Provider; +import javax.ws.rs.ext.WriterInterceptor; +import javax.ws.rs.ext.WriterInterceptorContext; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Provider +public class RequestClientWriterInterceptor implements WriterInterceptor { + + private static final Logger LOG = LoggerFactory.getLogger(RequestClientWriterInterceptor.class); + + @Override + public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException { + LOG.info("request writer interceptor in the client side"); + + context.getOutputStream() + .write(("Message added in the writer interceptor in the client side").getBytes()); + + context.proceed(); + } + +} diff --git a/jersey/src/main/java/com/baeldung/jersey/server/Greetings.java b/jersey/src/main/java/com/baeldung/jersey/server/Greetings.java new file mode 100644 index 0000000000..d0ea873db1 --- /dev/null +++ b/jersey/src/main/java/com/baeldung/jersey/server/Greetings.java @@ -0,0 +1,32 @@ +package com.baeldung.jersey.server; + +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; + +import com.baeldung.jersey.server.config.HelloBinding; + +@Path("/greetings") +public class Greetings { + + @GET + @HelloBinding + public String getHelloGreeting() { + return "hello"; + } + + @GET + @Path("/hi") + public String getHiGreeting() { + return "hi"; + } + + @POST + @Path("/custom") + public Response getCustomGreeting(String name) { + return Response.status(Status.OK.getStatusCode()) + .build(); + } +} diff --git a/jersey/src/main/java/com/baeldung/jersey/server/config/HelloBinding.java b/jersey/src/main/java/com/baeldung/jersey/server/config/HelloBinding.java new file mode 100644 index 0000000000..49b11adeab --- /dev/null +++ b/jersey/src/main/java/com/baeldung/jersey/server/config/HelloBinding.java @@ -0,0 +1,11 @@ +package com.baeldung.jersey.server.config; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import javax.ws.rs.NameBinding; + +@NameBinding +@Retention(RetentionPolicy.RUNTIME) +public @interface HelloBinding { +} diff --git a/jersey/src/main/java/com/baeldung/jersey/server/config/HelloDynamicBinding.java b/jersey/src/main/java/com/baeldung/jersey/server/config/HelloDynamicBinding.java new file mode 100644 index 0000000000..e56cdec140 --- /dev/null +++ b/jersey/src/main/java/com/baeldung/jersey/server/config/HelloDynamicBinding.java @@ -0,0 +1,31 @@ +package com.baeldung.jersey.server.config; + +import javax.ws.rs.container.DynamicFeature; +import javax.ws.rs.container.ResourceInfo; +import javax.ws.rs.core.FeatureContext; +import javax.ws.rs.ext.Provider; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.baeldung.jersey.server.Greetings; +import com.baeldung.jersey.server.filter.ResponseServerFilter; + +@Provider +public class HelloDynamicBinding implements DynamicFeature { + + private static final Logger LOG = LoggerFactory.getLogger(HelloDynamicBinding.class); + + @Override + public void configure(ResourceInfo resourceInfo, FeatureContext context) { + LOG.info("Hello dynamic binding"); + + if (Greetings.class.equals(resourceInfo.getResourceClass()) && resourceInfo.getResourceMethod() + .getName() + .contains("HiGreeting")) { + context.register(ResponseServerFilter.class); + } + + } + +} diff --git a/jersey/src/main/java/com/baeldung/jersey/server/config/ServerConfig.java b/jersey/src/main/java/com/baeldung/jersey/server/config/ServerConfig.java new file mode 100644 index 0000000000..4670cc291c --- /dev/null +++ b/jersey/src/main/java/com/baeldung/jersey/server/config/ServerConfig.java @@ -0,0 +1,14 @@ +package com.baeldung.jersey.server.config; + +import javax.ws.rs.ApplicationPath; + +import org.glassfish.jersey.server.ResourceConfig; + +@ApplicationPath("/*") +public class ServerConfig extends ResourceConfig { + + public ServerConfig() { + packages("com.baeldung.jersey.server"); + } + +} diff --git a/jersey/src/main/java/com/baeldung/jersey/server/filter/PrematchingRequestFilter.java b/jersey/src/main/java/com/baeldung/jersey/server/filter/PrematchingRequestFilter.java new file mode 100644 index 0000000000..181fa7f043 --- /dev/null +++ b/jersey/src/main/java/com/baeldung/jersey/server/filter/PrematchingRequestFilter.java @@ -0,0 +1,27 @@ +package com.baeldung.jersey.server.filter; + +import java.io.IOException; + +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.container.PreMatching; +import javax.ws.rs.ext.Provider; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Provider +@PreMatching +public class PrematchingRequestFilter implements ContainerRequestFilter { + + private static final Logger LOG = LoggerFactory.getLogger(PrematchingRequestFilter.class); + + @Override + public void filter(ContainerRequestContext ctx) throws IOException { + LOG.info("prematching filter"); + if (ctx.getMethod() + .equals("DELETE")) { + LOG.info("\"Deleting request"); + } + } +} diff --git a/jersey/src/main/java/com/baeldung/jersey/server/filter/ResponseServerFilter.java b/jersey/src/main/java/com/baeldung/jersey/server/filter/ResponseServerFilter.java new file mode 100644 index 0000000000..de0dcbdce7 --- /dev/null +++ b/jersey/src/main/java/com/baeldung/jersey/server/filter/ResponseServerFilter.java @@ -0,0 +1,23 @@ +package com.baeldung.jersey.server.filter; + +import java.io.IOException; + +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerResponseContext; +import javax.ws.rs.container.ContainerResponseFilter; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ResponseServerFilter implements ContainerResponseFilter { + + private static final Logger LOG = LoggerFactory.getLogger(ResponseServerFilter.class); + + @Override + public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException { + LOG.info("Response server filter"); + + responseContext.getHeaders() + .add("X-Test", "Filter test"); + } +} diff --git a/jersey/src/main/java/com/baeldung/jersey/server/filter/RestrictedOperationsRequestFilter.java b/jersey/src/main/java/com/baeldung/jersey/server/filter/RestrictedOperationsRequestFilter.java new file mode 100644 index 0000000000..cb0cd185f7 --- /dev/null +++ b/jersey/src/main/java/com/baeldung/jersey/server/filter/RestrictedOperationsRequestFilter.java @@ -0,0 +1,36 @@ +package com.baeldung.jersey.server.filter; + +import java.io.IOException; + +import javax.annotation.Priority; +import javax.ws.rs.Priorities; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.Provider; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.baeldung.jersey.server.config.HelloBinding; + +@Provider +@Priority(Priorities.AUTHORIZATION) +@HelloBinding +public class RestrictedOperationsRequestFilter implements ContainerRequestFilter { + + private static final Logger LOG = LoggerFactory.getLogger(RestrictedOperationsRequestFilter.class); + + @Override + public void filter(ContainerRequestContext ctx) throws IOException { + LOG.info("Restricted operations filter"); + if (ctx.getLanguage() != null && "EN".equals(ctx.getLanguage() + .getLanguage())) { + LOG.info("Aborting request"); + ctx.abortWith(Response.status(Response.Status.FORBIDDEN) + .entity("Cannot access") + .build()); + } + + } +} diff --git a/jersey/src/main/java/com/baeldung/jersey/server/interceptor/RequestServerReaderInterceptor.java b/jersey/src/main/java/com/baeldung/jersey/server/interceptor/RequestServerReaderInterceptor.java new file mode 100644 index 0000000000..e9cc57fc2a --- /dev/null +++ b/jersey/src/main/java/com/baeldung/jersey/server/interceptor/RequestServerReaderInterceptor.java @@ -0,0 +1,36 @@ +package com.baeldung.jersey.server.interceptor; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.stream.Collectors; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.ext.Provider; +import javax.ws.rs.ext.ReaderInterceptor; +import javax.ws.rs.ext.ReaderInterceptorContext; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Provider +public class RequestServerReaderInterceptor implements ReaderInterceptor { + + private static final Logger LOG = LoggerFactory.getLogger(RequestServerReaderInterceptor.class); + + @Override + public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException { + LOG.info("Request reader interceptor in the server side"); + + InputStream is = context.getInputStream(); + String body = new BufferedReader(new InputStreamReader(is)).lines() + .collect(Collectors.joining("\n")); + + context.setInputStream(new ByteArrayInputStream((body + " message added in server reader interceptor").getBytes())); + + return context.proceed(); + } + +} diff --git a/jersey/src/main/resources/logback.xml b/jersey/src/main/resources/logback.xml new file mode 100644 index 0000000000..d87a87bf53 --- /dev/null +++ b/jersey/src/main/resources/logback.xml @@ -0,0 +1,13 @@ + +4.0.0 + +com.baeldung +jersey +0.0.1-SNAPSHOT +war + ++ + +com.baeldung +parent-modules +1.0.0-SNAPSHOT ++ + +2.26 +1.7.25 +3.2.0 ++ + +jersey ++ ++ +maven-war-plugin +${maven-war-plugin.version} ++ +false ++ + ++ +org.glassfish.jersey.core +jersey-server +${jersey.version} ++ +org.glassfish.jersey.core +jersey-client +${jersey.version} ++ +org.glassfish.jersey.bundles +jaxrs-ri +${jersey.version} ++ + + + +org.slf4j +slf4j-api +${slf4j.version} ++ \ No newline at end of file diff --git a/jersey/src/test/java/com/baeldung/jersey/client/JerseyClientTest.java b/jersey/src/test/java/com/baeldung/jersey/client/JerseyClientTest.java new file mode 100644 index 0000000000..72ba4cc5b9 --- /dev/null +++ b/jersey/src/test/java/com/baeldung/jersey/client/JerseyClientTest.java @@ -0,0 +1,35 @@ +package com.baeldung.jersey.client; + +import javax.ws.rs.core.Response; + +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; + +@Ignore +public class JerseyClientTest { + + private static int HTTP_OK = 200; + + @Test + public void givenGreetingResource_whenCallingHelloGreeting_thenHelloReturned() { + String response = JerseyClient.getHelloGreeting(); + + Assert.assertEquals("hello", response); + } + + @Test + public void givenGreetingResource_whenCallingHiGreeting_thenHiReturned() { + String response = JerseyClient.getHiGreeting(); + + Assert.assertEquals("hi", response); + } + + @Test + public void givenGreetingResource_whenCallingCustomGreeting_thenCustomGreetingReturned() { + Response response = JerseyClient.getCustomGreeting(); + + Assert.assertEquals(HTTP_OK, response.getStatus()); + } + +} diff --git a/jgroups/README.md b/jgroups/README.md new file mode 100644 index 0000000000..bb2813c3d6 --- /dev/null +++ b/jgroups/README.md @@ -0,0 +1,15 @@ +## Reliable Messaging with JGroups Tutorial Project + +### Relevant Article: +- [Reliable Messaging with JGroups](http://www.baeldung.com/reliable-messaging-with-jgroups/) + +### Overview +This Maven project contains the Java code for the article linked above. + +### Package Organization +Java classes for the intro tutorial are in the org.baeldung.jgroups package. + + +### Running the tests + +``` diff --git a/jgroups/pom.xml b/jgroups/pom.xml new file mode 100644 index 0000000000..0e5971875e --- /dev/null +++ b/jgroups/pom.xml @@ -0,0 +1,36 @@ + ++ + ++ +%date [%thread] %-5level %logger{36} - %message%n + ++ ++ + diff --git a/jgroups/src/main/java/com/baeldung/jgroups/JGroupsMessenger.java b/jgroups/src/main/java/com/baeldung/jgroups/JGroupsMessenger.java new file mode 100644 index 0000000000..70eacabf1e --- /dev/null +++ b/jgroups/src/main/java/com/baeldung/jgroups/JGroupsMessenger.java @@ -0,0 +1,212 @@ +package com.baeldung.jgroups; + +import org.apache.commons.cli.*; +import org.jgroups.*; +import org.jgroups.util.Util; + +import java.io.*; +import java.util.List; +import java.util.Optional; + +public class JGroupsMessenger extends ReceiverAdapter { + + private JChannel channel; + private String userName; + private String clusterName; + private View lastView; + private boolean running = true; + + // Our shared state + private Integer messageCount = 0; + + /** + * Connect to a JGroups cluster using command line options + * @param args command line arguments + * @throws Exception + */ + private void start(String[] args) throws Exception { + processCommandline(args); + + // Create the channel + // This file could be moved, or made a command line option. + channel = new JChannel("src/main/resources/udp.xml"); + + // Set a name + channel.name(userName); + + // Register for callbacks + channel.setReceiver(this); + + // Ignore our messages + channel.setDiscardOwnMessages(true); + + // Connect + channel.connect(clusterName); + + // Start state transfer + channel.getState(null, 0); + + // Do the things + processInput(); + + // Clean up + channel.close(); + + } + + /** + * Quick and dirty implementaton of commons cli for command line args + * @param args the command line args + * @throws ParseException + */ + private void processCommandline(String[] args) throws ParseException { + + // Options, parser, friendly help + Options options = new Options(); + CommandLineParser parser = new DefaultParser(); + HelpFormatter formatter = new HelpFormatter(); + + options.addOption("u", "user", true, "User name") + .addOption("c", "cluster", true, "Cluster name"); + + CommandLine line = parser.parse(options, args); + + if (line.hasOption("user")) { + userName = line.getOptionValue("user"); + } else { + formatter.printHelp("JGroupsMessenger: need a user name.\n", options); + System.exit(-1); + } + + if (line.hasOption("cluster")) { + clusterName = line.getOptionValue("cluster"); + } else { + formatter.printHelp("JGroupsMessenger: need a cluster name.\n", options); + System.exit(-1); + } + } + + // Start it up + public static void main(String[] args) throws Exception { + new JGroupsMessenger().start(args); + } + + + @Override + public void viewAccepted(View newView) { + + // Save view if this is the first + if (lastView == null) { + System.out.println("Received initial view:"); + newView.forEach(System.out::println); + } else { + // Compare to last view + System.out.println("Received new view."); + + List newMembers = View.newMembers(lastView, newView); + System.out.println("New members: "); + newMembers.forEach(System.out::println); + + List exMembers = View.leftMembers(lastView, newView); + System.out.println("Exited members:"); + exMembers.forEach(System.out::println); + } + lastView = newView; + } + + /** + * Loop on console input until we see 'x' to exit + */ + private void processInput() throws Exception { + + BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); + while (running) { + try { + + // Get a destination,4.0.0 +jgroups +0.1-SNAPSHOT +jar +jgroups +Reliable Messaging with JGroups Tutorial + ++ + +com.baeldung +parent-modules +1.0.0-SNAPSHOT ++ + ++ + +org.jgroups +jgroups +4.0.10.Final ++ +commons-cli +commons-cli +1.4 ++ + +1.8 +UTF-8 +means broadcast + Address destination = null; + System.out.print("Enter a destination: "); + System.out.flush(); + String destinationName = in.readLine().toLowerCase(); + + if (destinationName.equals("x")) { + running = false; + continue; + } else if (!destinationName.isEmpty()) { + destination = getAddress(destinationName) + . orElseThrow(() -> new Exception("Destination not found")); + } + + // Accept a string to send + System.out.print("Enter a message: "); + System.out.flush(); + String line = in.readLine().toLowerCase(); + sendMessage(destination, line); + } catch (IOException ioe) { + running = false; + } + } + System.out.println("Exiting."); + } + + /** + * Send message from here + * @param destination the destination + * @param messageString the message + */ + private void sendMessage(Address destination, String messageString) { + try { + System.out.println("Sending " + messageString + " to " + destination); + Message message = new Message(destination, messageString); + channel.send(message); + } catch (Exception exception) { + System.err.println("Exception sending message: " + exception.getMessage()); + running = false; + } + } + + @Override + public void receive(Message message) { + // Print source and dest with message + String line = "Message received from: " + message.getSrc() + " to: " + message.getDest() + " -> " + message.getObject(); + + // Only track the count of broadcast messages + // Tracking direct message would make for a pointless state + if (message.getDest() == null) { + messageCount++; + System.out.println("Message count: " + messageCount); + } + + System.out.println(line); + } + + @Override + public void getState(OutputStream output) throws Exception { + // Serialize into the stream + Util.objectToStream(messageCount, new DataOutputStream(output)); + } + + @Override + public void setState(InputStream input) { + + // NOTE: since we know that incrementing the count and transferring the state + // is done inside the JChannel's thread, we don't have to worry about synchronizing + // messageCount. For production code it should be synchronized! + try { + // Deserialize + messageCount = Util.objectFromStream(new DataInputStream(input)); + } catch (Exception e) { + System.out.println("Error deserialing state!"); + } + System.out.println(messageCount + " is the current messagecount."); + } + + + private Optional getAddress(String name) { + View view = channel.view(); + return view.getMembers().stream().filter(address -> name.equals(address.toString())).findFirst(); + } + +} + + diff --git a/jgroups/src/main/resources/udp.xml b/jgroups/src/main/resources/udp.xml new file mode 100644 index 0000000000..5a9bfd0851 --- /dev/null +++ b/jgroups/src/main/resources/udp.xml @@ -0,0 +1,48 @@ + + diff --git a/jmeter/README.md b/jmeter/README.md index dec8364647..e3f9d1a4db 100644 --- a/jmeter/README.md +++ b/jmeter/README.md @@ -38,4 +38,9 @@ $ curl -X POST -H "Content-Type:application/json" -d '{ "firstName" : "Dassi", " Now with default configurations it will be available at: [http://localhost:8080](http://localhost:8080) -Enjoy it :) \ No newline at end of file +Enjoy it :) + +### Relevant Articles: + +- [Intro to Performance Testing using JMeter](http://www.baeldung.com/jmeter) +- [Configure Jenkins to Run and Show JMeter Tests](http://www.baeldung.com/jenkins-and-jmeter) diff --git a/json/pom.xml b/json/pom.xml index 958dd32f53..7c74d425af 100644 --- a/json/pom.xml +++ b/json/pom.xml @@ -31,6 +31,11 @@+ + + + + + + + + + + + + + + + + ${fastjson.version}