diff --git a/algorithms/README.md b/algorithms/README.md index 91d8b55dd6..b0c5ee9d77 100644 --- a/algorithms/README.md +++ b/algorithms/README.md @@ -11,3 +11,6 @@ - [String Search Algorithms for Large Texts](http://www.baeldung.com/java-full-text-search-algorithms) - [Test a Linked List for Cyclicity](http://www.baeldung.com/java-linked-list-cyclicity) - [Binary Search Algorithm in Java](http://www.baeldung.com/java-binary-search) +- [Bubble Sort in Java](http://www.baeldung.com/java-bubble-sort) +- [Introduction to JGraphT](http://www.baeldung.com/jgrapht) +- [Introduction to Minimax Algorithm](http://www.baeldung.com/java-minimax-algorithm) diff --git a/algorithms/src/main/java/com/baeldung/algorithms/editdistance/EditDistanceBase.java b/algorithms/src/main/java/com/baeldung/algorithms/editdistance/EditDistanceBase.java new file mode 100644 index 0000000000..e884c576c1 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/editdistance/EditDistanceBase.java @@ -0,0 +1,22 @@ +package com.baeldung.algorithms.editdistance; + +public class EditDistanceBase { + + public static int costOfSubstitution(char a, char b) { + if (a == b) { + return 0; + } + return 1; + } + + public static int min(int... numbers) { + int min = Integer.MAX_VALUE; + + for (int x : numbers) { + if (x < min) + min = x; + } + + return min; + } +} diff --git a/algorithms/src/main/java/com/baeldung/algorithms/editdistance/EditDistanceDynamicProgramming.java b/algorithms/src/main/java/com/baeldung/algorithms/editdistance/EditDistanceDynamicProgramming.java new file mode 100644 index 0000000000..163714002b --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/editdistance/EditDistanceDynamicProgramming.java @@ -0,0 +1,25 @@ +package com.baeldung.algorithms.editdistance; + +public class EditDistanceDynamicProgramming extends EditDistanceBase { + + public static int calculate(String x, String y) { + int[][] dp = new int[x.length() + 1][y.length() + 1]; + + for (int i = 0; i <= x.length(); i++) { + for (int j = 0; j <= y.length(); j++) { + if (i == 0) + dp[i][j] = j; + + else if (j == 0) + dp[i][j] = i; + + else { + dp[i][j] = min(dp[i - 1][j - 1] + costOfSubstitution(x.charAt(i - 1), y.charAt(j - 1)), dp[i - 1][j] + 1, dp[i][j - 1] + 1); + } + } + } + + return dp[x.length()][y.length()]; + } + +} diff --git a/algorithms/src/main/java/com/baeldung/algorithms/editdistance/EditDistanceRecursive.java b/algorithms/src/main/java/com/baeldung/algorithms/editdistance/EditDistanceRecursive.java new file mode 100644 index 0000000000..68e470147e --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/editdistance/EditDistanceRecursive.java @@ -0,0 +1,20 @@ +package com.baeldung.algorithms.editdistance; + +public class EditDistanceRecursive extends EditDistanceBase { + + public static int calculate(String x, String y) { + + if (x.isEmpty()) + return y.length(); + + if (y.isEmpty()) + return x.length(); + + int substitution = calculate(x.substring(1), y.substring(1)) + costOfSubstitution(x.charAt(0), y.charAt(0)); + int insertion = calculate(x, y.substring(1)) + 1; + int deletion = calculate(x.substring(1), y) + 1; + + return min(substitution, insertion, deletion); + } + +} diff --git a/algorithms/src/test/java/algorithms/MCTSTest.java b/algorithms/src/test/java/algorithms/MCTSTest.java deleted file mode 100644 index f969c26311..0000000000 --- a/algorithms/src/test/java/algorithms/MCTSTest.java +++ /dev/null @@ -1,92 +0,0 @@ -package algorithms; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.util.List; - -import org.junit.Before; -import org.junit.Test; - -import com.baeldung.algorithms.mcts.montecarlo.MonteCarloTreeSearch; -import com.baeldung.algorithms.mcts.montecarlo.State; -import com.baeldung.algorithms.mcts.montecarlo.UCT; -import com.baeldung.algorithms.mcts.tictactoe.Board; -import com.baeldung.algorithms.mcts.tictactoe.Position; -import com.baeldung.algorithms.mcts.tree.Tree; - -public class MCTSTest { - Tree gameTree; - MonteCarloTreeSearch mcts; - - @Before - public void initGameTree() { - gameTree = new Tree(); - mcts = new MonteCarloTreeSearch(); - } - - @Test - public void givenStats_whenGetUCTForNode_thenUCTMatchesWithManualData() { - double uctValue = 15.79; - assertEquals(UCT.uctValue(600, 300, 20), uctValue, 0.01); - } - - @Test - public void giveninitBoardState_whenGetAllPossibleStates_thenNonEmptyList() { - State initState = gameTree.getRoot().getState(); - List possibleStates = initState.getAllPossibleStates(); - assertTrue(possibleStates.size() > 0); - } - - @Test - public void givenEmptyBoard_whenPerformMove_thenLessAvailablePossitions() { - Board board = new Board(); - int initAvailablePositions = board.getEmptyPositions().size(); - board.performMove(Board.P1, new Position(1, 1)); - int availablePositions = board.getEmptyPositions().size(); - assertTrue(initAvailablePositions > availablePositions); - } - - @Test - public void givenEmptyBoard_whenSimulateInterAIPlay_thenGameDraw() { - Board board = new Board(); - - int player = Board.P1; - int totalMoves = Board.DEFAULT_BOARD_SIZE * Board.DEFAULT_BOARD_SIZE; - for (int i = 0; i < totalMoves; i++) { - board = mcts.findNextMove(board, player); - if (board.checkStatus() != -1) { - break; - } - player = 3 - player; - } - int winStatus = board.checkStatus(); - assertEquals(winStatus, Board.DRAW); - } - - @Test - public void givenEmptyBoard_whenLevel1VsLevel3_thenLevel3WinsOrDraw() { - Board board = new Board(); - MonteCarloTreeSearch mcts1 = new MonteCarloTreeSearch(); - mcts1.setLevel(1); - MonteCarloTreeSearch mcts3 = new MonteCarloTreeSearch(); - mcts3.setLevel(3); - - int player = Board.P1; - int totalMoves = Board.DEFAULT_BOARD_SIZE * Board.DEFAULT_BOARD_SIZE; - for (int i = 0; i < totalMoves; i++) { - if (player == Board.P1) - board = mcts3.findNextMove(board, player); - else - board = mcts1.findNextMove(board, player); - - if (board.checkStatus() != -1) { - break; - } - player = 3 - player; - } - int winStatus = board.checkStatus(); - assertTrue(winStatus == Board.DRAW || winStatus == Board.P1); - } - -} diff --git a/algorithms/src/test/java/com/baeldung/algorithms/editdistance/EditDistanceDataProvider.java b/algorithms/src/test/java/com/baeldung/algorithms/editdistance/EditDistanceDataProvider.java new file mode 100644 index 0000000000..89bd871616 --- /dev/null +++ b/algorithms/src/test/java/com/baeldung/algorithms/editdistance/EditDistanceDataProvider.java @@ -0,0 +1,21 @@ +package com.baeldung.algorithms.editdistance; + +import org.junit.runners.Parameterized.Parameters; + +import java.util.Arrays; +import java.util.Collection; + +public class EditDistanceDataProvider { + + @Parameters + public static Collection getLists() { + return Arrays.asList(new Object[][] { + { "", "", 0 }, + { "ago", "", 3 }, + { "", "do", 2 }, + { "abc", "adc", 1 }, + { "peek", "pesek", 1 }, + { "sunday", "saturday", 3 } + }); + } +} diff --git a/algorithms/src/test/java/com/baeldung/algorithms/editdistance/EditDistanceTest.java b/algorithms/src/test/java/com/baeldung/algorithms/editdistance/EditDistanceTest.java new file mode 100644 index 0000000000..1594f73a73 --- /dev/null +++ b/algorithms/src/test/java/com/baeldung/algorithms/editdistance/EditDistanceTest.java @@ -0,0 +1,31 @@ +package com.baeldung.algorithms.editdistance; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class EditDistanceTest extends EditDistanceDataProvider { + + String x; + String y; + int result; + + public EditDistanceTest(String a, String b, int res) { + super(); + x = a; + y = b; + result = res; + } + + @Test + public void testEditDistance_RecursiveImplementation() { + Assert.assertEquals(result, EditDistanceRecursive.calculate(x, y)); + } + + @Test + public void testEditDistance_givenDynamicProgrammingImplementation() { + Assert.assertEquals(result, EditDistanceDynamicProgramming.calculate(x, y)); + } +} diff --git a/animal-sniffer-mvn-plugin/README.md b/animal-sniffer-mvn-plugin/README.md index ff12555376..4c7c381da4 100644 --- a/animal-sniffer-mvn-plugin/README.md +++ b/animal-sniffer-mvn-plugin/README.md @@ -1 +1,3 @@ ## Relevant articles: + +[Introduction to Animal Sniffer Maven Plugin](http://www.baeldung.com/maven-animal-sniffer) diff --git a/apache-cayenne/README.md b/apache-cayenne/README.md index ff12555376..610d1233b7 100644 --- a/apache-cayenne/README.md +++ b/apache-cayenne/README.md @@ -1 +1,4 @@ ## Relevant articles: + +- [Advanced Querying in Apache Cayenne](http://www.baeldung.com/apache-cayenne-query) +- [Introduction to Apache Cayenne ORM](http://www.baeldung.com/apache-cayenne-orm) diff --git a/apache-cxf/README.md b/apache-cxf/README.md index d8f1912070..1e66ce5da8 100644 --- a/apache-cxf/README.md +++ b/apache-cxf/README.md @@ -1,2 +1,5 @@ ## Relevant Articles: - [Introduction to Apache CXF Aegis Data Binding](http://www.baeldung.com/aegis-data-binding-in-apache-cxf) +- [Apache CXF Support for RESTful Web Services](http://www.baeldung.com/apache-cxf-rest-api) +- [A Guide to Apache CXF with Spring](http://www.baeldung.com/apache-cxf-with-spring) +- [Introduction to Apache CXF](http://www.baeldung.com/introduction-to-apache-cxf) diff --git a/apache-spark/pom.xml b/apache-spark/pom.xml new file mode 100644 index 0000000000..1f95150ee7 --- /dev/null +++ b/apache-spark/pom.xml @@ -0,0 +1,44 @@ + + 4.0.0 + + com.baeldung + apache-spark + 1.0-SNAPSHOT + jar + + apache-spark + http://maven.apache.org + + + UTF-8 + + + + + + org.apache.spark + spark-core_2.10 + 2.2.0 + + + junit + junit + 3.8.1 + test + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.7.0 + + 1.8 + 1.8 + + + + + diff --git a/apache-spark/src/main/java/com/baeldung/WordCount.java b/apache-spark/src/main/java/com/baeldung/WordCount.java new file mode 100644 index 0000000000..ec1dedcb69 --- /dev/null +++ b/apache-spark/src/main/java/com/baeldung/WordCount.java @@ -0,0 +1,53 @@ +package com.baeldung; + +import java.util.Arrays; +import java.util.List; +import java.util.regex.Pattern; + +import org.apache.spark.SparkConf; +import org.apache.spark.api.java.JavaPairRDD; +import org.apache.spark.api.java.JavaRDD; +import org.apache.spark.api.java.JavaSparkContext; +import org.apache.spark.api.java.function.*; +import org.apache.spark.api.java.function.Function2; +import org.apache.spark.api.java.function.PairFunction; + +import scala.Tuple2; + +public class WordCount { + + private static final Pattern SPACE = Pattern.compile(" "); + + public static void main(String[] args) throws Exception { + if (args.length < 1) { + System.err.println("Usage: JavaWordCount "); + System.exit(1); + } + SparkConf sparkConf = new SparkConf().setAppName("JavaWordCount").setMaster("local"); + JavaSparkContext ctx = new JavaSparkContext(sparkConf); + JavaRDD lines = ctx.textFile(args[0], 1); + + JavaRDD words = lines.flatMap(s -> Arrays.asList(SPACE.split(s)).iterator()); + JavaPairRDD ones = words.mapToPair( + new PairFunction() { + @Override + public Tuple2 call(String s) { + return new Tuple2<>(s, 1); + } + }); + + JavaPairRDD counts = ones.reduceByKey( + new Function2() { + @Override + public Integer call(Integer i1, Integer i2) { + return i1 + i2; + } + }); + + List> output = counts.collect(); + for (Tuple2 tuple : output) { + System.out.println(tuple._1() + ": " + tuple._2()); + } + ctx.stop(); +} +} diff --git a/apache-spark/src/main/resources/spark_example.txt b/apache-spark/src/main/resources/spark_example.txt new file mode 100644 index 0000000000..10fd71dc31 --- /dev/null +++ b/apache-spark/src/main/resources/spark_example.txt @@ -0,0 +1,3 @@ +Hello from Baeldung +Keep Learning Spark +Bye from Baeldung \ No newline at end of file diff --git a/asm/README.md b/asm/README.md index ff12555376..50d9c34324 100644 --- a/asm/README.md +++ b/asm/README.md @@ -1 +1,3 @@ -## Relevant articles: +### Relevant Articles: + +- [A Guide to Java Bytecode Manipulation with ASM](http://www.baeldung.com/java-asm) diff --git a/atomix/README.md b/atomix/README.md index ff12555376..fb22eec8dc 100644 --- a/atomix/README.md +++ b/atomix/README.md @@ -1 +1,3 @@ ## Relevant articles: + +- [Introduction to Atomix](http://www.baeldung.com/atomix) diff --git a/aws/README.md b/aws/README.md index 10db004765..b6d0b79d91 100644 --- a/aws/README.md +++ b/aws/README.md @@ -1,3 +1,6 @@ ### Relevant articles - [AWS Lambda Using DynamoDB With Java](http://www.baeldung.com/aws-lambda-dynamodb-java) +- [AWS S3 with Java](http://www.baeldung.com/aws-s3-java) +- [AWS Lambda With Java](http://www.baeldung.com/java-aws-lambda) + diff --git a/camel-api/README.md b/camel-api/README.md index fe8dadcfe1..0e85db4a7f 100644 --- a/camel-api/README.md +++ b/camel-api/README.md @@ -13,3 +13,7 @@ and a BODY Payload like {"id": 1,"name": "World"} and we will get a return code of 201 and the response: Hello, World - if the transform() method from Application class is uncommented and the process() method is commented or return code of 201 and the response: {"id": 10,"name": "Hello, World"} - if the transform() method from Application class is commented and the process() method is uncommented + +## Relevant articles: + +- [Apache Camel with Spring Boot](http://www.baeldung.com/apache-camel-spring-boot) diff --git a/core-java/src/main/java/com/baeldung/array/ArrayInitializer.java b/core-java/src/main/java/com/baeldung/array/ArrayInitializer.java new file mode 100644 index 0000000000..fd00c74e7f --- /dev/null +++ b/core-java/src/main/java/com/baeldung/array/ArrayInitializer.java @@ -0,0 +1,74 @@ +package com.baeldung.array; + +import java.util.Arrays; + +public class ArrayInitializer { + + public static int[] initializeArrayInLoop() { + int array[] = new int[5]; + for (int i = 0; i < array.length; i++) + array[i] = i + 2; + return array; + } + + public static int[][] initializeMultiDimensionalArrayInLoop() { + int array[][] = new int[2][5]; + for (int i = 0; i < 2; i++) + for (int j = 0; j < 5; j++) + array[i][j] = j + 1; + return array; + } + + public static String[] initializeArrayAtTimeOfDeclarationMethod1() { + String array[] = new String[] { "Toyota", "Mercedes", "BMW", "Volkswagen", "Skoda" }; + return array; + } + + public static int[] initializeArrayAtTimeOfDeclarationMethod2() { + int[] array = new int[] { 1, 2, 3, 4, 5 }; + return array; + } + + public static int[] initializeArrayAtTimeOfDeclarationMethod3() { + int array[] = { 1, 2, 3, 4, 5 }; + return array; + } + + public static long[] initializeArrayUsingArraysFill() { + long array[] = new long[5]; + Arrays.fill(array, 30); + return array; + } + + public static int[] initializeArrayRangeUsingArraysFill() { + int array[] = new int[5]; + Arrays.fill(array, 0, 3, -50); + return array; + } + + public static int[] initializeArrayUsingArraysCopy() { + int array[] = { 1, 2, 3, 4, 5 }; + int[] copy = Arrays.copyOf(array, 5); + return copy; + } + + public static int[] initializeLargerArrayUsingArraysCopy() { + int array[] = { 1, 2, 3, 4, 5 }; + int[] copy = Arrays.copyOf(array, 6); + return copy; + } + + public static int[] initializeArrayUsingArraysSetAll() { + int[] array = new int[20]; + + for (int i = 0; i < 20; i++) { + Arrays.setAll(array, p -> { + if (p > 9) + return 0; + else + return p; + }); + } + return array; + } +} diff --git a/core-java/src/test/java/com/baeldung/array/ArrayInitializerTest.java b/core-java/src/test/java/com/baeldung/array/ArrayInitializerTest.java new file mode 100644 index 0000000000..d3afad7b00 --- /dev/null +++ b/core-java/src/test/java/com/baeldung/array/ArrayInitializerTest.java @@ -0,0 +1,68 @@ +package com.baeldung.array; + +import static com.baeldung.array.ArrayInitializer.initializeArrayAtTimeOfDeclarationMethod1; +import static com.baeldung.array.ArrayInitializer.initializeArrayAtTimeOfDeclarationMethod2; +import static com.baeldung.array.ArrayInitializer.initializeArrayAtTimeOfDeclarationMethod3; +import static com.baeldung.array.ArrayInitializer.initializeArrayInLoop; +import static com.baeldung.array.ArrayInitializer.initializeArrayRangeUsingArraysFill; +import static com.baeldung.array.ArrayInitializer.initializeArrayUsingArraysCopy; +import static com.baeldung.array.ArrayInitializer.initializeArrayUsingArraysFill; +import static com.baeldung.array.ArrayInitializer.initializeArrayUsingArraysSetAll; +import static com.baeldung.array.ArrayInitializer.initializeLargerArrayUsingArraysCopy; +import static com.baeldung.array.ArrayInitializer.initializeMultiDimensionalArrayInLoop; +import static org.junit.Assert.assertArrayEquals; + +import org.junit.Test; + +public class ArrayInitializerTest { + + @Test + public void whenInitializeArrayInLoop_thenCorrect() { + assertArrayEquals(new int[] { 2, 3, 4, 5, 6 }, initializeArrayInLoop()); + } + + @Test + public void whenInitializeMultiDimensionalArrayInLoop_thenCorrect() { + assertArrayEquals(new int[][] { { 1, 2, 3, 4, 5 }, { 1, 2, 3, 4, 5 } }, initializeMultiDimensionalArrayInLoop()); + } + + @Test + public void whenInitializeArrayAtTimeOfDeclarationMethod1_thenCorrect() { + assertArrayEquals(new String[] { "Toyota", "Mercedes", "BMW", "Volkswagen", "Skoda" }, initializeArrayAtTimeOfDeclarationMethod1()); + } + + @Test + public void whenInitializeArrayAtTimeOfDeclarationMethod2_thenCorrect() { + assertArrayEquals(new int[] { 1, 2, 3, 4, 5 }, initializeArrayAtTimeOfDeclarationMethod2()); + } + + @Test + public void whenInitializeArrayAtTimeOfDeclarationMethod3_thenCorrect() { + assertArrayEquals(new int[] { 1, 2, 3, 4, 5 }, initializeArrayAtTimeOfDeclarationMethod3()); + } + + @Test + public void whenInitializeArrayUsingArraysFill_thenCorrect() { + assertArrayEquals(new long[] { 30, 30, 30, 30, 30 }, initializeArrayUsingArraysFill()); + } + + @Test + public void whenInitializeArrayRangeUsingArraysFill_thenCorrect() { + assertArrayEquals(new int[] { -50, -50, -50, 0, 0 }, initializeArrayRangeUsingArraysFill()); + } + + @Test + public void whenInitializeArrayRangeUsingArraysCopy_thenCorrect() { + assertArrayEquals(new int[] { 1, 2, 3, 4, 5 }, initializeArrayUsingArraysCopy()); + } + + @Test + public void whenInitializeLargerArrayRangeUsingArraysCopy_thenCorrect() { + assertArrayEquals(new int[] { 1, 2, 3, 4, 5, 0 }, initializeLargerArrayUsingArraysCopy()); + } + + @Test + public void whenInitializeLargerArrayRangeUsingArraysSetAll_thenCorrect() { + assertArrayEquals(new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, initializeArrayUsingArraysSetAll()); + } +} diff --git a/drools/README.MD b/drools/README.MD index 4ece7608fc..e3f00f5047 100644 --- a/drools/README.MD +++ b/drools/README.MD @@ -1,3 +1,4 @@ ### Relevant Articles: -[Introduction to Drools](http://www.baeldung.com/drools) -[Drools Using Rules from Excel Files](http://www.baeldung.com/drools-excel) +- [Introduction to Drools](http://www.baeldung.com/drools) +- [Drools Using Rules from Excel Files](http://www.baeldung.com/drools-excel) +- [Drools Using Rules from Excel Files](http://www.baeldung.com/drools-excel) diff --git a/feign/README.md b/feign/README.md index 4d6964a73a..da04c40cdc 100644 --- a/feign/README.md +++ b/feign/README.md @@ -5,4 +5,6 @@ This is the implementation of a [spring-hypermedia-api][1] client using Feign. [1]: https://github.com/eugenp/spring-hypermedia-api ### Relevant Articles: + - [Intro to Feign](http://www.baeldung.com/intro-to-feign) +- [Introduction to SLF4J](http://www.baeldung.com/slf4j-with-log4j2-logback) diff --git a/graphql/graphql-java/README.md b/graphql/graphql-java/README.md index ff12555376..0033524209 100644 --- a/graphql/graphql-java/README.md +++ b/graphql/graphql-java/README.md @@ -1 +1,3 @@ ## Relevant articles: + +- [Introduction to GraphQL](http://www.baeldung.com/graphql) diff --git a/guava21/README.md b/guava21/README.md index 2a54416e41..68c1ac4a8e 100644 --- a/guava21/README.md +++ b/guava21/README.md @@ -1,3 +1,4 @@ ### Relevant articles: - [New Stream, Comparator and Collector Functionality in Guava 21](http://www.baeldung.com/guava-21-new) - [New in Guava 21 common.util.concurrent](http://www.baeldung.com/guava-21-util-concurrent) +- [Zipping Collections in Java](http://www.baeldung.com/java-collections-zip) diff --git a/handling-spring-static-resources/README.md b/handling-spring-static-resources/README.md index d8f64bc427..c12e0272d4 100644 --- a/handling-spring-static-resources/README.md +++ b/handling-spring-static-resources/README.md @@ -1,3 +1,4 @@ ### Relevant Articles: - [Cachable Static Assets with Spring MVC](http://www.baeldung.com/cachable-static-assets-with-spring-mvc) - [Minification of JS and CSS Assets with Maven](http://www.baeldung.com/maven-minification-of-js-and-css-assets) +- [Serve Static Resources with Spring](http://www.baeldung.com/spring-mvc-static-resources) diff --git a/httpclient/README.md b/httpclient/README.md index 2a98c2feac..93e0f3c9e1 100644 --- a/httpclient/README.md +++ b/httpclient/README.md @@ -20,3 +20,5 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring - [HttpAsyncClient Tutorial](http://www.baeldung.com/httpasyncclient-tutorial) - [HttpClient 4 Tutorial](http://www.baeldung.com/httpclient-guide) - [Advanced HttpClient Configuration](http://www.baeldung.com/httpclient-advanced-config) +- [HttpClient 4 – Do Not Follow Redirects](http://www.baeldung.com/httpclient-stop-follow-redirect) +- [HttpClient 4 – Setting a Custom User-Agent](http://www.baeldung.com/httpclient-user-agent-header) diff --git a/jackson/README.md b/jackson/README.md index 51919f6769..a710a1689e 100644 --- a/jackson/README.md +++ b/jackson/README.md @@ -28,3 +28,10 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring - [A Guide to Optional with Jackson](http://www.baeldung.com/jackson-optional) - [Map Serialization and Deserialization with Jackson](http://www.baeldung.com/jackson-map) - [Jackson Streaming API](http://www.baeldung.com/jackson-streaming-api) +- [Jackson – JsonMappingException (No serializer found for class)](http://www.baeldung.com/jackson-jsonmappingexception) +- [How To Serialize Enums as JSON Objects with Jackson](http://www.baeldung.com/jackson-serialize-enums) +- [Jackson – Marshall String to JsonNode](http://www.baeldung.com/jackson-json-to-jsonnode) +- [Ignore Null Fields with Jackson](http://www.baeldung.com/jackson-ignore-null-fields) +- [Jackson – Unmarshall to Collection/Array](http://www.baeldung.com/jackson-collection-array) +- [Jackson – Change Name of Field](http://www.baeldung.com/jackson-name-of-property) +- [Serialize Only Fields that meet a Custom Criteria with Jackson](http://www.baeldung.com/jackson-serialize-field-custom-criteria) diff --git a/jackson/pom.xml b/jackson/pom.xml index f970b6a68c..001fde5021 100644 --- a/jackson/pom.xml +++ b/jackson/pom.xml @@ -129,7 +129,7 @@ - 2.9.0 + 2.9.2 19.0 diff --git a/jackson/src/test/java/com/baeldung/jackson/deserialization/nested/DeserializeWithNestedPropertiesUnitTest.java b/jackson/src/test/java/com/baeldung/jackson/deserialization/nested/DeserializeWithNestedPropertiesUnitTest.java new file mode 100644 index 0000000000..037bc7e880 --- /dev/null +++ b/jackson/src/test/java/com/baeldung/jackson/deserialization/nested/DeserializeWithNestedPropertiesUnitTest.java @@ -0,0 +1,70 @@ +package com.baeldung.jackson.deserialization.nested; + +import static org.junit.Assert.*; + +import java.io.IOException; + +import org.junit.Test; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; + +public class DeserializeWithNestedPropertiesUnitTest { + + private String SOURCE_JSON = "{\"id\":\"957c43f2-fa2e-42f9-bf75-6e3d5bb6960a\",\"name\":\"The Best Product\",\"brand\":{\"id\":\"9bcd817d-0141-42e6-8f04-e5aaab0980b6\",\"name\":\"ACME Products\",\"owner\":{\"id\":\"b21a80b1-0c09-4be3-9ebd-ea3653511c13\",\"name\":\"Ultimate Corp, Inc.\"}}}"; + + @Test + public void whenUsingAnnotations_thenOk() throws IOException { + Product product = new ObjectMapper().readerFor(Product.class) + .readValue(SOURCE_JSON); + + assertEquals(product.getName(), "The Best Product"); + assertEquals(product.getBrandName(), "ACME Products"); + assertEquals(product.getOwnerName(), "Ultimate Corp, Inc."); + } + + @Test + public void whenUsingJsonNode_thenOk() throws IOException { + JsonNode productNode = new ObjectMapper().readTree(SOURCE_JSON); + + Product product = new Product(); + product.setId(productNode.get("id") + .textValue()); + product.setName(productNode.get("name") + .textValue()); + product.setBrandName(productNode.get("brand") + .get("name") + .textValue()); + product.setOwnerName(productNode.get("brand") + .get("owner") + .get("name") + .textValue()); + + assertEquals(product.getName(), "The Best Product"); + assertEquals(product.getBrandName(), "ACME Products"); + assertEquals(product.getOwnerName(), "Ultimate Corp, Inc."); + } + + @Test + public void whenUsingDeserializerManuallyRegistered_thenOk() throws IOException { + ObjectMapper mapper = new ObjectMapper(); + SimpleModule module = new SimpleModule(); + module.addDeserializer(Product.class, new ProductDeserializer()); + mapper.registerModule(module); + + Product product = mapper.readValue(SOURCE_JSON, Product.class); + assertEquals(product.getName(), "The Best Product"); + assertEquals(product.getBrandName(), "ACME Products"); + assertEquals(product.getOwnerName(), "Ultimate Corp, Inc."); + } + + @Test + public void whenUsingDeserializerAutoRegistered_thenOk() throws IOException { + ObjectMapper mapper = new ObjectMapper(); + Product product = mapper.readValue(SOURCE_JSON, Product.class); + assertEquals(product.getName(), "The Best Product"); + assertEquals(product.getBrandName(), "ACME Products"); + assertEquals(product.getOwnerName(), "Ultimate Corp, Inc."); + } +} diff --git a/jackson/src/test/java/com/baeldung/jackson/deserialization/nested/Product.java b/jackson/src/test/java/com/baeldung/jackson/deserialization/nested/Product.java new file mode 100644 index 0000000000..0020afea0f --- /dev/null +++ b/jackson/src/test/java/com/baeldung/jackson/deserialization/nested/Product.java @@ -0,0 +1,55 @@ +package com.baeldung.jackson.deserialization.nested; + +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; + +@JsonDeserialize(using = ProductDeserializer.class) +public class Product { + + private String id; + private String name; + private String brandName; + private String ownerName; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getBrandName() { + return brandName; + } + + public void setBrandName(String brandName) { + this.brandName = brandName; + } + + public String getOwnerName() { + return ownerName; + } + + public void setOwnerName(String ownerName) { + this.ownerName = ownerName; + } + + @SuppressWarnings("unchecked") + @JsonProperty("brand") + private void unpackNested(Map brand) { + this.brandName = (String) brand.get("name"); + Map owner = (Map) brand.get("owner"); + this.ownerName = owner.get("name"); + } +} \ No newline at end of file diff --git a/jackson/src/test/java/com/baeldung/jackson/deserialization/nested/ProductDeserializer.java b/jackson/src/test/java/com/baeldung/jackson/deserialization/nested/ProductDeserializer.java new file mode 100644 index 0000000000..daabae6cda --- /dev/null +++ b/jackson/src/test/java/com/baeldung/jackson/deserialization/nested/ProductDeserializer.java @@ -0,0 +1,40 @@ +package com.baeldung.jackson.deserialization.nested; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; + +@SuppressWarnings("serial") +public class ProductDeserializer extends StdDeserializer { + + public ProductDeserializer() { + this(null); + } + + public ProductDeserializer(Class vc) { + super(vc); + } + + @Override + public Product deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { + JsonNode productNode = jp.getCodec() + .readTree(jp); + Product product = new Product(); + product.setId(productNode.get("id") + .textValue()); + product.setName(productNode.get("name") + .textValue()); + product.setBrandName(productNode.get("brand") + .get("name") + .textValue()); + product.setOwnerName(productNode.get("brand") + .get("owner") + .get("name") + .textValue()); + return product; + } +} diff --git a/javaxval/README.md b/javaxval/README.md index b5001789f1..c6a6eb7345 100644 --- a/javaxval/README.md +++ b/javaxval/README.md @@ -7,3 +7,4 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring ### Relevant Articles: - [Java Bean Validation Basics](http://www.baeldung.com/javax-validation) +- [Validating Container Elements with Bean Validation 2.0](http://www.baeldung.com/bean-validation-container-elements) diff --git a/jhipster/README.md b/jhipster/README.md index ff12555376..f3655f8ec1 100644 --- a/jhipster/README.md +++ b/jhipster/README.md @@ -1 +1,4 @@ ## Relevant articles: + +- [JHipster with a Microservice Architecture](http://www.baeldung.com/jhipster-microservices) +- [Intro to JHipster](http://www.baeldung.com/jhipster) diff --git a/jjwt/README.md b/jjwt/README.md index 47b51038a8..54a5226056 100644 --- a/jjwt/README.md +++ b/jjwt/README.md @@ -42,4 +42,8 @@ Available commands (assumes httpie - https://github.com/jkbrzt/httpie): Parse passed in JWT enforcing the 'iss' registered claim and the 'hasMotorcycle' custom claim ``` -The Baeldung post that compliments this repo can be found [here](http://www.baeldung.com/) \ No newline at end of file +The Baeldung post that compliments this repo can be found [here](http://www.baeldung.com/) + +## Relevant articles: + +- [Supercharge Java Authentication with JSON Web Tokens (JWTs)](http://www.baeldung.com/java-json-web-tokens-jjwt) diff --git a/jmh/README.md b/jmh/README.md index ff12555376..2a2c6a173a 100644 --- a/jmh/README.md +++ b/jmh/README.md @@ -1 +1,3 @@ ## Relevant articles: + +- [Microbenchmarking with Java](http://www.baeldung.com/java-microbenchmark-harness) diff --git a/jooby/README.md b/jooby/README.md index ff12555376..032e595354 100644 --- a/jooby/README.md +++ b/jooby/README.md @@ -1 +1,3 @@ ## Relevant articles: + +- [Introduction to Jooby Project](http://www.baeldung.com/jooby) diff --git a/json-path/README.md b/json-path/README.md index ff12555376..3563dcf880 100644 --- a/json-path/README.md +++ b/json-path/README.md @@ -1 +1,3 @@ ## Relevant articles: + +- [Introduction to JsonPath](http://www.baeldung.com/guide-to-jayway-jsonpath) diff --git a/libraries-data/README.md b/libraries-data/README.md index ca70c61146..ceb0a1d5f7 100644 --- a/libraries-data/README.md +++ b/libraries-data/README.md @@ -1,2 +1,4 @@ ### Relevant articles - [Introduction to Reladomo](http://www.baeldung.com/reladomo) +- [Introduction to ORMLite](http://www.baeldung.com/ormlite) +- [Introduction To Kryo](http://www.baeldung.com/kryo) diff --git a/libraries/README.md b/libraries/README.md index 243baf2c73..c6bbb5634c 100644 --- a/libraries/README.md +++ b/libraries/README.md @@ -8,9 +8,9 @@ - [Embedded Jetty Server in Java](http://www.baeldung.com/jetty-embedded) - [Introduction to Apache Flink with Java](http://www.baeldung.com/apache-flink) - [Introduction to JSONassert](http://www.baeldung.com/jsonassert) -- [Intro to JaVer](http://www.baeldung.com/javers) +- [Intro to JaVers](http://www.baeldung.com/javers) - [Introduction to Apache Commons Math](http://www.baeldung.com/apache-commons-math) -- [Intro to JaVer](http://www.baeldung.com/serenity-bdd) +- [Intro to Serenity BDD](http://www.baeldung.com/serenity-bdd) - [Introduction to Netty](http://www.baeldung.com/netty) - [Merging Streams in Java](http://www.baeldung.com/java-merge-streams) - [Serenity BDD and Screenplay](http://www.baeldung.com/serenity-screenplay) @@ -53,6 +53,7 @@ - [Using Pairs in Java](http://www.baeldung.com/java-pairs) - [Apache Commons Collections Bag](http://www.baeldung.com/apache-commons-bag) - [Introduction to Caffeine](http://www.baeldung.com/java-caching-caffeine) ++-[Introduction to Chronicle Queue](http://www.baeldung.com/java-chronicle-queue) The libraries module contains examples related to small libraries that are relatively easy to use and does not require any separate module of its own. diff --git a/linkrest/README.md b/linkrest/README.md index ff12555376..33cf930b18 100644 --- a/linkrest/README.md +++ b/linkrest/README.md @@ -1 +1,3 @@ ## Relevant articles: + +- [Guide to LinkRest](http://www.baeldung.com/linkrest) diff --git a/mocks/README.md b/mocks/README.md index ff12555376..15370b812b 100644 --- a/mocks/README.md +++ b/mocks/README.md @@ -1 +1,7 @@ ## Relevant articles: + +- [Introduction to MockServer](http://www.baeldung.com/mockserver) +- [JMockit Advanced Usage](http://www.baeldung.com/jmockit-advanced-usage) +- [A Guide to JMockit Expectations](http://www.baeldung.com/jmockit-expectations) +- [JMockit 101](http://www.baeldung.com/jmockit-101) +- [Mockito vs EasyMock vs JMockit](http://www.baeldung.com/mockito-vs-easymock-vs-jmockit) diff --git a/mockserver/README.md b/mockserver/README.md index ff12555376..a8bc5cfc98 100644 --- a/mockserver/README.md +++ b/mockserver/README.md @@ -1 +1,3 @@ ## Relevant articles: + +- [Introduction to MockServer](http://www.baeldung.com/mockserver) diff --git a/rule-engines/README.md b/rule-engines/README.md index ff12555376..6d3c0e93b4 100644 --- a/rule-engines/README.md +++ b/rule-engines/README.md @@ -1 +1,3 @@ ## Relevant articles: + +- [List of Rules Engines in Java](http://www.baeldung.com/java-rule-engines) diff --git a/saas/README.md b/saas/README.md index ff12555376..4e0eeea974 100644 --- a/saas/README.md +++ b/saas/README.md @@ -1 +1,3 @@ ## Relevant articles: + +- [JIRA REST API Integration](http://www.baeldung.com/jira-rest-api) diff --git a/spring-drools/README.md b/spring-drools/README.md index ff12555376..00b35a4624 100644 --- a/spring-drools/README.md +++ b/spring-drools/README.md @@ -1 +1,3 @@ ## Relevant articles: + +- [Drools Spring Integration](http://www.baeldung.com/drools-spring-integration) diff --git a/spring-jpa/pom.xml b/spring-jpa/pom.xml index 6a67d44b48..960dcbc588 100644 --- a/spring-jpa/pom.xml +++ b/spring-jpa/pom.xml @@ -113,6 +113,11 @@ guava ${guava.version} + + org.assertj + assertj-core + ${assertj.version} + @@ -180,6 +185,7 @@ 21.0 3.5 + 3.8.0 4.4.5 4.5.2 diff --git a/spring-jpa/src/main/java/org/baeldung/config/StudentJPAH2Config.java b/spring-jpa/src/main/java/org/baeldung/config/StudentJPAH2Config.java new file mode 100644 index 0000000000..439c6cb602 --- /dev/null +++ b/spring-jpa/src/main/java/org/baeldung/config/StudentJPAH2Config.java @@ -0,0 +1,69 @@ +package org.baeldung.config; + +import java.util.Properties; + +import javax.persistence.EntityManagerFactory; +import javax.sql.DataSource; + +import org.baeldung.extended.persistence.dao.ExtendedRepositoryImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import org.springframework.core.env.Environment; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +@Configuration +@EnableJpaRepositories(basePackages = "org.baeldung.extended.persistence.dao", repositoryBaseClass = ExtendedRepositoryImpl.class) +@PropertySource("persistence-student-h2.properties") +@EnableTransactionManagement +public class StudentJPAH2Config { + + @Autowired + private Environment env; + + @Bean + public DataSource dataSource() { + final DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName")); + dataSource.setUrl(env.getProperty("jdbc.url")); + dataSource.setUsername(env.getProperty("jdbc.user")); + dataSource.setPassword(env.getProperty("jdbc.pass")); + + return dataSource; + } + + @Bean + public LocalContainerEntityManagerFactoryBean entityManagerFactory() { + final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); + em.setDataSource(dataSource()); + em.setPackagesToScan(new String[] { "org.baeldung.inmemory.persistence.model" }); + em.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); + em.setJpaProperties(additionalProperties()); + return em; + } + + @Bean + JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { + JpaTransactionManager transactionManager = new JpaTransactionManager(); + transactionManager.setEntityManagerFactory(entityManagerFactory); + return transactionManager; + } + + final Properties additionalProperties() { + final Properties hibernateProperties = new Properties(); + + hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto")); + hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect")); + hibernateProperties.setProperty("hibernate.show_sql", env.getProperty("hibernate.show_sql")); + hibernateProperties.setProperty("hibernate.cache.use_second_level_cache", env.getProperty("hibernate.cache.use_second_level_cache")); + hibernateProperties.setProperty("hibernate.cache.use_query_cache", env.getProperty("hibernate.cache.use_query_cache")); + + return hibernateProperties; + } +} diff --git a/spring-jpa/src/main/java/org/baeldung/config/StudentJpaConfig.java b/spring-jpa/src/main/java/org/baeldung/config/StudentJpaConfig.java index a40f180a62..8021691716 100644 --- a/spring-jpa/src/main/java/org/baeldung/config/StudentJpaConfig.java +++ b/spring-jpa/src/main/java/org/baeldung/config/StudentJpaConfig.java @@ -18,7 +18,7 @@ import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; import org.springframework.transaction.annotation.EnableTransactionManagement; @Configuration -@EnableJpaRepositories(basePackages = "org.baeldung.persistence.dao") +@EnableJpaRepositories(basePackages = "org.baeldung.inmemory.persistence.dao") @PropertySource("persistence-student.properties") @EnableTransactionManagement public class StudentJpaConfig { @@ -41,7 +41,7 @@ public class StudentJpaConfig { public LocalContainerEntityManagerFactoryBean entityManagerFactory() { final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); em.setDataSource(dataSource()); - em.setPackagesToScan(new String[] { "org.baeldung.persistence.model" }); + em.setPackagesToScan(new String[] { "org.baeldung.inmemory.persistence.model" }); em.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); em.setJpaProperties(additionalProperties()); return em; diff --git a/spring-jpa/src/main/java/org/baeldung/extended/persistence/dao/ExtendedRepository.java b/spring-jpa/src/main/java/org/baeldung/extended/persistence/dao/ExtendedRepository.java new file mode 100644 index 0000000000..9c9c12029a --- /dev/null +++ b/spring-jpa/src/main/java/org/baeldung/extended/persistence/dao/ExtendedRepository.java @@ -0,0 +1,12 @@ +package org.baeldung.extended.persistence.dao; + +import java.io.Serializable; +import java.util.List; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.repository.NoRepositoryBean; + +@NoRepositoryBean +public interface ExtendedRepository extends JpaRepository { + public List findByAttributeContainsText(String attributeName, String text); +} diff --git a/spring-jpa/src/main/java/org/baeldung/extended/persistence/dao/ExtendedRepositoryImpl.java b/spring-jpa/src/main/java/org/baeldung/extended/persistence/dao/ExtendedRepositoryImpl.java new file mode 100644 index 0000000000..0dd32757d7 --- /dev/null +++ b/spring-jpa/src/main/java/org/baeldung/extended/persistence/dao/ExtendedRepositoryImpl.java @@ -0,0 +1,36 @@ +package org.baeldung.extended.persistence.dao; + +import java.io.Serializable; +import java.util.List; + +import javax.persistence.EntityManager; +import javax.persistence.TypedQuery; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Root; +import javax.transaction.Transactional; + +import org.springframework.data.jpa.repository.support.JpaEntityInformation; +import org.springframework.data.jpa.repository.support.SimpleJpaRepository; + +public class ExtendedRepositoryImpl extends SimpleJpaRepository implements ExtendedRepository { + + private EntityManager entityManager; + + public ExtendedRepositoryImpl(JpaEntityInformation entityInformation, EntityManager entityManager) { + super(entityInformation, entityManager); + this.entityManager = entityManager; + } + + @Transactional + public List findByAttributeContainsText(String attributeName, String text) { + CriteriaBuilder builder = entityManager.getCriteriaBuilder(); + CriteriaQuery query = builder.createQuery(getDomainClass()); + Root root = query.from(getDomainClass()); + query.select(root) + .where(builder.like(root. get(attributeName), "%" + text + "%")); + TypedQuery q = entityManager.createQuery(query); + return q.getResultList(); + } + +} diff --git a/spring-jpa/src/main/java/org/baeldung/extended/persistence/dao/ExtendedStudentRepository.java b/spring-jpa/src/main/java/org/baeldung/extended/persistence/dao/ExtendedStudentRepository.java new file mode 100644 index 0000000000..7e2efc72bc --- /dev/null +++ b/spring-jpa/src/main/java/org/baeldung/extended/persistence/dao/ExtendedStudentRepository.java @@ -0,0 +1,6 @@ +package org.baeldung.extended.persistence.dao; + +import org.baeldung.inmemory.persistence.model.Student; + +public interface ExtendedStudentRepository extends ExtendedRepository { +} diff --git a/spring-jpa/src/main/java/org/baeldung/persistence/dao/StudentRepository.java b/spring-jpa/src/main/java/org/baeldung/inmemory/persistence/dao/StudentRepository.java similarity index 57% rename from spring-jpa/src/main/java/org/baeldung/persistence/dao/StudentRepository.java rename to spring-jpa/src/main/java/org/baeldung/inmemory/persistence/dao/StudentRepository.java index af484b442c..bfcf6f5cdc 100644 --- a/spring-jpa/src/main/java/org/baeldung/persistence/dao/StudentRepository.java +++ b/spring-jpa/src/main/java/org/baeldung/inmemory/persistence/dao/StudentRepository.java @@ -1,8 +1,7 @@ -package org.baeldung.persistence.dao; +package org.baeldung.inmemory.persistence.dao; +import org.baeldung.inmemory.persistence.model.Student; import org.springframework.data.jpa.repository.JpaRepository; -import org.baeldung.persistence.model.Student; - public interface StudentRepository extends JpaRepository { } diff --git a/spring-jpa/src/main/java/org/baeldung/persistence/model/Student.java b/spring-jpa/src/main/java/org/baeldung/inmemory/persistence/model/Student.java similarity index 91% rename from spring-jpa/src/main/java/org/baeldung/persistence/model/Student.java rename to spring-jpa/src/main/java/org/baeldung/inmemory/persistence/model/Student.java index 437eeac5bb..b50fe9122e 100644 --- a/spring-jpa/src/main/java/org/baeldung/persistence/model/Student.java +++ b/spring-jpa/src/main/java/org/baeldung/inmemory/persistence/model/Student.java @@ -1,4 +1,4 @@ -package org.baeldung.persistence.model; +package org.baeldung.inmemory.persistence.model; import javax.persistence.Entity; import javax.persistence.Id; diff --git a/spring-jpa/src/main/resources/persistence-student-h2.properties b/spring-jpa/src/main/resources/persistence-student-h2.properties new file mode 100644 index 0000000000..e1d6bfa45a --- /dev/null +++ b/spring-jpa/src/main/resources/persistence-student-h2.properties @@ -0,0 +1,12 @@ +# jdbc.X +jdbc.driverClassName=org.h2.Driver +jdbc.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1 +jdbc.user=sa +# jdbc.pass= + +# hibernate.X +hibernate.dialect=org.hibernate.dialect.H2Dialect +hibernate.show_sql=true +hibernate.hbm2ddl.auto=create-drop +hibernate.cache.use_second_level_cache=false +hibernate.cache.use_query_cache=false \ No newline at end of file diff --git a/spring-jpa/src/test/java/org/baeldung/persistence/repository/ExtendedStudentRepositoryIntegrationTest.java b/spring-jpa/src/test/java/org/baeldung/persistence/repository/ExtendedStudentRepositoryIntegrationTest.java new file mode 100644 index 0000000000..0970daa0ee --- /dev/null +++ b/spring-jpa/src/test/java/org/baeldung/persistence/repository/ExtendedStudentRepositoryIntegrationTest.java @@ -0,0 +1,39 @@ +package org.baeldung.persistence.repository; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; + +import javax.annotation.Resource; + +import org.baeldung.config.StudentJPAH2Config; +import org.baeldung.extended.persistence.dao.ExtendedStudentRepository; +import org.baeldung.inmemory.persistence.model.Student; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = { StudentJPAH2Config.class}) +public class ExtendedStudentRepositoryIntegrationTest { + @Resource + private ExtendedStudentRepository extendedStudentRepository; + + @Before + public void setup(){ + Student student = new Student(1, "john"); + extendedStudentRepository.save(student); + Student student2 = new Student(2, "johnson"); + extendedStudentRepository.save(student2); + Student student3 = new Student(3, "tom"); + extendedStudentRepository.save(student3); + } + + @Test + public void givenStudents_whenFindByName_thenGetOk(){ + List students = extendedStudentRepository.findByAttributeContainsText("name", "john"); + assertThat(students.size()).isEqualTo(2); + } +} diff --git a/spring-jpa/src/test/java/org/baeldung/persistence/repository/InMemoryDBIntegrationTest.java b/spring-jpa/src/test/java/org/baeldung/persistence/repository/InMemoryDBIntegrationTest.java index 1fcc4be45d..8380ab5434 100644 --- a/spring-jpa/src/test/java/org/baeldung/persistence/repository/InMemoryDBIntegrationTest.java +++ b/spring-jpa/src/test/java/org/baeldung/persistence/repository/InMemoryDBIntegrationTest.java @@ -1,8 +1,8 @@ package org.baeldung.persistence.repository; import org.baeldung.config.StudentJpaConfig; -import org.baeldung.persistence.dao.StudentRepository; -import org.baeldung.persistence.model.Student; +import org.baeldung.inmemory.persistence.dao.StudentRepository; +import org.baeldung.inmemory.persistence.model.Student; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; diff --git a/spring-rest-simple/README.md b/spring-rest-simple/README.md index ff12555376..916676feb5 100644 --- a/spring-rest-simple/README.md +++ b/spring-rest-simple/README.md @@ -1 +1,7 @@ ## Relevant articles: + +- [Guide to UriComponentsBuilder in Spring](http://www.baeldung.com/spring-uricomponentsbuilder) +- [Returning Custom Status Codes from Spring Controllers](http://www.baeldung.com/spring-mvc-controller-custom-http-status-code) +- [The Guide to RestTemplate](http://www.baeldung.com/rest-template) +- [Spring RequestMapping](http://www.baeldung.com/spring-requestmapping) +- [ETags for REST with Spring](http://www.baeldung.com/etags-for-rest-with-spring) diff --git a/spring-security-mvc-custom/README.md b/spring-security-mvc-custom/README.md index 14bac6c454..2c0be4768e 100644 --- a/spring-security-mvc-custom/README.md +++ b/spring-security-mvc-custom/README.md @@ -8,7 +8,10 @@ The "REST With Spring" Classes: http://github.learnspringsecurity.com ### Relevant Articles: - [Spring Security Remember Me](http://www.baeldung.com/spring-security-remember-me) - [Redirect to different pages after Login with Spring Security](http://www.baeldung.com/spring_redirect_after_login) - +- [Changing Spring Model Parameters with Handler Interceptor](http://www.baeldung.com/spring-model-parameters-with-handler-interceptor) +- [Introduction to Spring MVC HandlerInterceptor](http://www.baeldung.com/spring-mvc-handlerinterceptor) +- [Using a Custom Spring MVC’s Handler Interceptor to Manage Sessions](http://www.baeldung.com/spring-mvc-custom-handler-interceptor) +- [A Guide to CSRF Protection in Spring Security](http://www.baeldung.com/spring-security-csrf) ### Build the Project ``` diff --git a/spring-security-mvc-custom/pom.xml b/spring-security-mvc-custom/pom.xml index 4a7e9419cc..805792b795 100644 --- a/spring-security-mvc-custom/pom.xml +++ b/spring-security-mvc-custom/pom.xml @@ -113,6 +113,40 @@ + + + com.fasterxml.jackson.core + jackson-databind + ${jackson-databind.version} + + + + org.apache.commons + commons-lang3 + ${commons-lang3.version} + + + + com.google.guava + guava + ${guava.version} + + + + + + org.springframework + spring-test + ${org.springframework.version} + test + + + + org.springframework.security + spring-security-test + ${org.springframework.security.version} + test + @@ -174,7 +208,8 @@ 19.0 3.5 - + 2.9.1 + 4.5.2 4.4.5 diff --git a/spring-security-mvc-custom/src/main/java/org/baeldung/spring/MvcConfig.java b/spring-security-mvc-custom/src/main/java/org/baeldung/spring/MvcConfig.java index 2229516633..3b97afc22d 100644 --- a/spring-security-mvc-custom/src/main/java/org/baeldung/spring/MvcConfig.java +++ b/spring-security-mvc-custom/src/main/java/org/baeldung/spring/MvcConfig.java @@ -1,9 +1,14 @@ package org.baeldung.spring; +import org.baeldung.web.interceptor.LoggerInterceptor; +import org.baeldung.web.interceptor.SessionTimerInterceptor; +import org.baeldung.web.interceptor.UserInterceptor; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import org.springframework.web.servlet.view.InternalResourceViewResolver; @@ -11,6 +16,7 @@ import org.springframework.web.servlet.view.JstlView; @EnableWebMvc @Configuration +@ComponentScan("org.baeldung.web.controller") public class MvcConfig extends WebMvcConfigurerAdapter { public MvcConfig() { @@ -28,6 +34,7 @@ public class MvcConfig extends WebMvcConfigurerAdapter { registry.addViewController("/login.html"); registry.addViewController("/homepage.html"); registry.addViewController("/console.html"); + registry.addViewController("/csrfHome.html"); } @Bean @@ -40,4 +47,11 @@ public class MvcConfig extends WebMvcConfigurerAdapter { return bean; } + + @Override + public void addInterceptors(final InterceptorRegistry registry) { + registry.addInterceptor(new LoggerInterceptor()); + registry.addInterceptor(new UserInterceptor()); + registry.addInterceptor(new SessionTimerInterceptor()); + } } \ No newline at end of file diff --git a/spring-security-rest-full/src/main/java/org/baeldung/web/controller/BankController.java b/spring-security-mvc-custom/src/main/java/org/baeldung/web/controller/BankController.java similarity index 97% rename from spring-security-rest-full/src/main/java/org/baeldung/web/controller/BankController.java rename to spring-security-mvc-custom/src/main/java/org/baeldung/web/controller/BankController.java index e87d5f3dd4..1a4322c611 100644 --- a/spring-security-rest-full/src/main/java/org/baeldung/web/controller/BankController.java +++ b/spring-security-mvc-custom/src/main/java/org/baeldung/web/controller/BankController.java @@ -12,7 +12,6 @@ import org.springframework.web.bind.annotation.ResponseStatus; // to test csrf @Controller -@RequestMapping(value = "/auth/") public class BankController { private final Logger logger = LoggerFactory.getLogger(getClass()); diff --git a/spring-security-mvc-custom/src/main/java/org/baeldung/web/controller/FooController.java b/spring-security-mvc-custom/src/main/java/org/baeldung/web/controller/FooController.java new file mode 100644 index 0000000000..5a3c85d220 --- /dev/null +++ b/spring-security-mvc-custom/src/main/java/org/baeldung/web/controller/FooController.java @@ -0,0 +1,59 @@ +package org.baeldung.web.controller; + +import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic; + +import java.util.Arrays; +import java.util.List; + +import javax.servlet.http.HttpServletResponse; + +import org.baeldung.web.dto.Foo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.util.UriComponentsBuilder; + +@Controller +@RequestMapping(value = "/auth/foos") +public class FooController { + + @Autowired + private ApplicationEventPublisher eventPublisher; + + public FooController() { + super(); + } + + // API + + // read - single + + @RequestMapping(value = "/{id}", method = RequestMethod.GET) + @ResponseBody + public Foo findById(@PathVariable("id") final Long id, final UriComponentsBuilder uriBuilder, final HttpServletResponse response) { + return new Foo(randomAlphabetic(6)); + } + + // read - multiple + + @RequestMapping(method = RequestMethod.GET) + @ResponseBody + public List findAll() { + return Arrays.asList(new Foo(randomAlphabetic(6))); + } + + // write - just for test + @RequestMapping(method = RequestMethod.POST) + @ResponseStatus(HttpStatus.CREATED) + @ResponseBody + public Foo create(@RequestBody final Foo foo) { + return foo; + } +} \ No newline at end of file diff --git a/spring-security-mvc-custom/src/main/java/org/baeldung/web/dto/Foo.java b/spring-security-mvc-custom/src/main/java/org/baeldung/web/dto/Foo.java new file mode 100644 index 0000000000..02283e7df9 --- /dev/null +++ b/spring-security-mvc-custom/src/main/java/org/baeldung/web/dto/Foo.java @@ -0,0 +1,80 @@ +package org.baeldung.web.dto; + +import java.io.Serializable; + +public class Foo implements Serializable { + + private long id; + + private String name; + + public Foo() { + super(); + } + + public Foo(final String name) { + super(); + + this.name = name; + } + + // API + + public long getId() { + return id; + } + + public void setId(final long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + // + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Foo other = (Foo) obj; + if (name == null) { + if (other.name != null) { + return false; + } + } else if (!name.equals(other.name)) { + return false; + } + return true; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("Foo [name=") + .append(name) + .append("]"); + return builder.toString(); + } + +} \ No newline at end of file diff --git a/spring-security-rest-full/src/main/java/org/baeldung/web/interceptor/LoggerInterceptor.java b/spring-security-mvc-custom/src/main/java/org/baeldung/web/interceptor/LoggerInterceptor.java similarity index 100% rename from spring-security-rest-full/src/main/java/org/baeldung/web/interceptor/LoggerInterceptor.java rename to spring-security-mvc-custom/src/main/java/org/baeldung/web/interceptor/LoggerInterceptor.java diff --git a/spring-security-rest-full/src/main/java/org/baeldung/web/interceptor/SessionTimerInterceptor.java b/spring-security-mvc-custom/src/main/java/org/baeldung/web/interceptor/SessionTimerInterceptor.java similarity index 100% rename from spring-security-rest-full/src/main/java/org/baeldung/web/interceptor/SessionTimerInterceptor.java rename to spring-security-mvc-custom/src/main/java/org/baeldung/web/interceptor/SessionTimerInterceptor.java diff --git a/spring-security-rest-full/src/main/java/org/baeldung/web/interceptor/UserInterceptor.java b/spring-security-mvc-custom/src/main/java/org/baeldung/web/interceptor/UserInterceptor.java similarity index 100% rename from spring-security-rest-full/src/main/java/org/baeldung/web/interceptor/UserInterceptor.java rename to spring-security-mvc-custom/src/main/java/org/baeldung/web/interceptor/UserInterceptor.java diff --git a/spring-security-mvc-custom/src/main/resources/webSecurityConfig.xml b/spring-security-mvc-custom/src/main/resources/webSecurityConfig.xml index f31f36655c..f2ecaba5c8 100644 --- a/spring-security-mvc-custom/src/main/resources/webSecurityConfig.xml +++ b/spring-security-mvc-custom/src/main/resources/webSecurityConfig.xml @@ -33,4 +33,8 @@ + + \ No newline at end of file diff --git a/spring-security-rest-full/src/main/webapp/WEB-INF/view/csrfHome.jsp b/spring-security-mvc-custom/src/main/webapp/WEB-INF/view/csrfHome.jsp similarity index 100% rename from spring-security-rest-full/src/main/webapp/WEB-INF/view/csrfHome.jsp rename to spring-security-mvc-custom/src/main/webapp/WEB-INF/view/csrfHome.jsp diff --git a/spring-security-rest-full/src/test/java/org/baeldung/security/csrf/CsrfAbstractIntegrationTest.java b/spring-security-mvc-custom/src/test/java/org/baeldung/security/csrf/CsrfAbstractIntegrationTest.java similarity index 93% rename from spring-security-rest-full/src/test/java/org/baeldung/security/csrf/CsrfAbstractIntegrationTest.java rename to spring-security-mvc-custom/src/test/java/org/baeldung/security/csrf/CsrfAbstractIntegrationTest.java index 6e70f979c8..44424bf7f9 100644 --- a/spring-security-rest-full/src/test/java/org/baeldung/security/csrf/CsrfAbstractIntegrationTest.java +++ b/spring-security-mvc-custom/src/test/java/org/baeldung/security/csrf/CsrfAbstractIntegrationTest.java @@ -5,7 +5,7 @@ import static org.springframework.security.test.web.servlet.request.SecurityMock import javax.servlet.Filter; -import org.baeldung.persistence.model.Foo; +import org.baeldung.web.dto.Foo; import org.junit.Before; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -14,15 +14,15 @@ import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.RequestPostProcessor; import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.transaction.annotation.Transactional; import org.springframework.web.context.WebApplicationContext; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; + + @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration -@Transactional public abstract class CsrfAbstractIntegrationTest { @Autowired diff --git a/spring-security-mvc-custom/src/test/java/org/baeldung/security/csrf/CsrfDisabledIntegrationTest.java b/spring-security-mvc-custom/src/test/java/org/baeldung/security/csrf/CsrfDisabledIntegrationTest.java new file mode 100644 index 0000000000..1d16e08514 --- /dev/null +++ b/spring-security-mvc-custom/src/test/java/org/baeldung/security/csrf/CsrfDisabledIntegrationTest.java @@ -0,0 +1,25 @@ +package org.baeldung.security.csrf; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.baeldung.security.spring.SecurityWithoutCsrfConfig; +import org.baeldung.spring.MvcConfig; +import org.junit.Test; +import org.springframework.http.MediaType; +import org.springframework.test.context.ContextConfiguration; + +@ContextConfiguration(classes = { SecurityWithoutCsrfConfig.class, MvcConfig.class }) +public class CsrfDisabledIntegrationTest extends CsrfAbstractIntegrationTest { + + @Test + public void givenNotAuth_whenAddFoo_thenUnauthorized() throws Exception { + mvc.perform(post("/auth/foos").contentType(MediaType.APPLICATION_JSON).content(createFoo())).andExpect(status().isUnauthorized()); + } + + @Test + public void givenAuth_whenAddFoo_thenCreated() throws Exception { + mvc.perform(post("/auth/foos").contentType(MediaType.APPLICATION_JSON).content(createFoo()).with(testUser())).andExpect(status().isCreated()); + } + +} diff --git a/spring-security-rest-full/src/test/java/org/baeldung/security/csrf/CsrfEnabledIntegrationTest.java b/spring-security-mvc-custom/src/test/java/org/baeldung/security/csrf/CsrfEnabledIntegrationTest.java similarity index 90% rename from spring-security-rest-full/src/test/java/org/baeldung/security/csrf/CsrfEnabledIntegrationTest.java rename to spring-security-mvc-custom/src/test/java/org/baeldung/security/csrf/CsrfEnabledIntegrationTest.java index 939b745de8..9d882973bd 100644 --- a/spring-security-rest-full/src/test/java/org/baeldung/security/csrf/CsrfEnabledIntegrationTest.java +++ b/spring-security-mvc-custom/src/test/java/org/baeldung/security/csrf/CsrfEnabledIntegrationTest.java @@ -5,13 +5,12 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.baeldung.security.spring.SecurityWithCsrfConfig; -import org.baeldung.spring.PersistenceConfig; -import org.baeldung.spring.WebConfig; +import org.baeldung.spring.MvcConfig; import org.junit.Test; import org.springframework.http.MediaType; import org.springframework.test.context.ContextConfiguration; -@ContextConfiguration(classes = { SecurityWithCsrfConfig.class, PersistenceConfig.class, WebConfig.class }) +@ContextConfiguration(classes = { SecurityWithCsrfConfig.class, MvcConfig.class }) public class CsrfEnabledIntegrationTest extends CsrfAbstractIntegrationTest { @Test diff --git a/spring-security-rest-full/src/test/java/org/baeldung/security/spring/SecurityWithCsrfConfig.java b/spring-security-mvc-custom/src/test/java/org/baeldung/security/spring/SecurityWithCsrfConfig.java similarity index 82% rename from spring-security-rest-full/src/test/java/org/baeldung/security/spring/SecurityWithCsrfConfig.java rename to spring-security-mvc-custom/src/test/java/org/baeldung/security/spring/SecurityWithCsrfConfig.java index 97ae1f1dd2..9600977e37 100644 --- a/spring-security-rest-full/src/test/java/org/baeldung/security/spring/SecurityWithCsrfConfig.java +++ b/spring-security-mvc-custom/src/test/java/org/baeldung/security/spring/SecurityWithCsrfConfig.java @@ -1,8 +1,5 @@ package org.baeldung.security.spring; -import org.baeldung.web.error.CustomAccessDeniedHandler; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; @@ -12,14 +9,10 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @Configuration -@EnableAutoConfiguration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityWithCsrfConfig extends WebSecurityConfigurerAdapter { - @Autowired - private CustomAccessDeniedHandler accessDeniedHandler; - public SecurityWithCsrfConfig() { super(); } @@ -46,8 +39,6 @@ public class SecurityWithCsrfConfig extends WebSecurityConfigurerAdapter { .and() .httpBasic() .and() - .exceptionHandling().accessDeniedHandler(accessDeniedHandler) - .and() .headers().cacheControl().disable() ; // @formatter:on diff --git a/spring-security-rest-full/src/main/java/org/baeldung/security/spring/SecurityWithoutCsrfConfig.java b/spring-security-mvc-custom/src/test/java/org/baeldung/security/spring/SecurityWithoutCsrfConfig.java similarity index 72% rename from spring-security-rest-full/src/main/java/org/baeldung/security/spring/SecurityWithoutCsrfConfig.java rename to spring-security-mvc-custom/src/test/java/org/baeldung/security/spring/SecurityWithoutCsrfConfig.java index f1a78d1472..f7dbd5b42c 100644 --- a/spring-security-rest-full/src/main/java/org/baeldung/security/spring/SecurityWithoutCsrfConfig.java +++ b/spring-security-mvc-custom/src/test/java/org/baeldung/security/spring/SecurityWithoutCsrfConfig.java @@ -1,8 +1,5 @@ package org.baeldung.security.spring; -import org.baeldung.web.error.CustomAccessDeniedHandler; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; @@ -12,16 +9,10 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @Configuration -@EnableAutoConfiguration -// @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) -// @ImportResource({ "classpath:webSecurityConfig.xml" }) public class SecurityWithoutCsrfConfig extends WebSecurityConfigurerAdapter { - @Autowired - private CustomAccessDeniedHandler accessDeniedHandler; - public SecurityWithoutCsrfConfig() { super(); } @@ -42,18 +33,15 @@ public class SecurityWithoutCsrfConfig extends WebSecurityConfigurerAdapter { protected void configure(final HttpSecurity http) throws Exception { // @formatter:off http - .csrf().disable() .authorizeRequests() - .antMatchers("/auth/admin/*").hasRole("ADMIN") - .antMatchers("/auth/*").hasAnyRole("ADMIN","USER") - .antMatchers("/*").permitAll() + .antMatchers("/auth/admin/*").hasAnyRole("ROLE_ADMIN") + .anyRequest().authenticated() .and() .httpBasic() .and() - // .exceptionHandling().accessDeniedPage("/my-error-page") - .exceptionHandling().accessDeniedHandler(accessDeniedHandler) - .and() .headers().cacheControl().disable() + .and() + .csrf().disable() ; // @formatter:on } diff --git a/spring-security-rest-full/src/test/java/org/baeldung/web/interceptor/LoggerInterceptorIntegrationTest.java b/spring-security-mvc-custom/src/test/java/org/baeldung/web/interceptor/LoggerInterceptorIntegrationTest.java similarity index 83% rename from spring-security-rest-full/src/test/java/org/baeldung/web/interceptor/LoggerInterceptorIntegrationTest.java rename to spring-security-mvc-custom/src/test/java/org/baeldung/web/interceptor/LoggerInterceptorIntegrationTest.java index 44dc860e62..c33c9a04e8 100644 --- a/spring-security-rest-full/src/test/java/org/baeldung/web/interceptor/LoggerInterceptorIntegrationTest.java +++ b/spring-security-mvc-custom/src/test/java/org/baeldung/web/interceptor/LoggerInterceptorIntegrationTest.java @@ -4,8 +4,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.baeldung.security.spring.SecurityWithoutCsrfConfig; -import org.baeldung.spring.PersistenceConfig; -import org.baeldung.spring.WebConfig; +import org.baeldung.spring.MvcConfig; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -16,13 +15,11 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.transaction.annotation.Transactional; import org.springframework.web.context.WebApplicationContext; @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration -@Transactional -@ContextConfiguration(classes = { SecurityWithoutCsrfConfig.class, PersistenceConfig.class, WebConfig.class }) +@ContextConfiguration(classes = { SecurityWithoutCsrfConfig.class, MvcConfig.class }) public class LoggerInterceptorIntegrationTest { @Autowired @@ -46,7 +43,8 @@ public class LoggerInterceptorIntegrationTest { */ @Test public void testInterceptors() throws Exception { - mockMvc.perform(get("/graph.html")).andExpect(status().isOk()); + mockMvc.perform(get("/login.html")) + .andExpect(status().isOk()); } } diff --git a/spring-security-rest-full/src/test/java/org/baeldung/web/interceptor/SessionTimerInterceptorIntegrationTest.java b/spring-security-mvc-custom/src/test/java/org/baeldung/web/interceptor/SessionTimerInterceptorIntegrationTest.java similarity index 78% rename from spring-security-rest-full/src/test/java/org/baeldung/web/interceptor/SessionTimerInterceptorIntegrationTest.java rename to spring-security-mvc-custom/src/test/java/org/baeldung/web/interceptor/SessionTimerInterceptorIntegrationTest.java index d62fab0670..bdc1be2c44 100644 --- a/spring-security-rest-full/src/test/java/org/baeldung/web/interceptor/SessionTimerInterceptorIntegrationTest.java +++ b/spring-security-mvc-custom/src/test/java/org/baeldung/web/interceptor/SessionTimerInterceptorIntegrationTest.java @@ -6,8 +6,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import javax.servlet.http.HttpSession; import org.baeldung.security.spring.SecurityWithoutCsrfConfig; -import org.baeldung.spring.PersistenceConfig; -import org.baeldung.spring.WebConfig; +import org.baeldung.spring.MvcConfig; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -20,13 +19,11 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.transaction.annotation.Transactional; import org.springframework.web.context.WebApplicationContext; @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration -@Transactional -@ContextConfiguration(classes = { SecurityWithoutCsrfConfig.class, PersistenceConfig.class, WebConfig.class }) +@ContextConfiguration(classes = { SecurityWithoutCsrfConfig.class, MvcConfig.class }) @WithMockUser(username = "admin", roles = { "USER", "ADMIN" }) public class SessionTimerInterceptorIntegrationTest { @@ -47,9 +44,14 @@ public class SessionTimerInterceptorIntegrationTest { */ @Test public void testInterceptors() throws Exception { - HttpSession session = mockMvc.perform(get("/auth/admin")).andExpect(status().is2xxSuccessful()).andReturn().getRequest().getSession(); + HttpSession session = mockMvc.perform(get("/auth/foos")) + .andExpect(status().is2xxSuccessful()) + .andReturn() + .getRequest() + .getSession(); Thread.sleep(51000); - mockMvc.perform(get("/auth/admin").session((MockHttpSession) session)).andExpect(status().is2xxSuccessful()); + mockMvc.perform(get("/auth/foos").session((MockHttpSession) session)) + .andExpect(status().is2xxSuccessful()); } } diff --git a/spring-security-rest-full/src/test/java/org/baeldung/web/interceptor/UserInterceptorIntegrationTest.java b/spring-security-mvc-custom/src/test/java/org/baeldung/web/interceptor/UserInterceptorIntegrationTest.java similarity index 84% rename from spring-security-rest-full/src/test/java/org/baeldung/web/interceptor/UserInterceptorIntegrationTest.java rename to spring-security-mvc-custom/src/test/java/org/baeldung/web/interceptor/UserInterceptorIntegrationTest.java index f995f86145..a85fd999a6 100644 --- a/spring-security-rest-full/src/test/java/org/baeldung/web/interceptor/UserInterceptorIntegrationTest.java +++ b/spring-security-mvc-custom/src/test/java/org/baeldung/web/interceptor/UserInterceptorIntegrationTest.java @@ -1,8 +1,10 @@ package org.baeldung.web.interceptor; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + import org.baeldung.security.spring.SecurityWithoutCsrfConfig; -import org.baeldung.spring.PersistenceConfig; -import org.baeldung.spring.WebConfig; +import org.baeldung.spring.MvcConfig; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -14,16 +16,11 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.transaction.annotation.Transactional; import org.springframework.web.context.WebApplicationContext; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration -@Transactional -@ContextConfiguration(classes = { SecurityWithoutCsrfConfig.class, PersistenceConfig.class, WebConfig.class }) +@ContextConfiguration(classes = { SecurityWithoutCsrfConfig.class, MvcConfig.class }) @WithMockUser(username = "admin", roles = { "USER", "ADMIN" }) public class UserInterceptorIntegrationTest { @@ -46,7 +43,8 @@ public class UserInterceptorIntegrationTest { */ @Test public void testInterceptors() throws Exception { - mockMvc.perform(get("/auth/admin")).andExpect(status().is2xxSuccessful()); + mockMvc.perform(get("/auth/foos")) + .andExpect(status().is2xxSuccessful()); } } diff --git a/spring-security-rest-full/.springBeans b/spring-security-rest-full/.springBeans index f100c6afbe..b01040d91b 100644 --- a/spring-security-rest-full/.springBeans +++ b/spring-security-rest-full/.springBeans @@ -1,18 +1,16 @@ 1 - + - java:org.baeldung.security.spring.SecurityWithoutCsrfConfig src/main/webapp/WEB-INF/api-servlet.xml java:org.baeldung.spring.Application - java:org.baeldung.security.spring.SecurityWithCsrfConfig diff --git a/spring-security-rest-full/README.md b/spring-security-rest-full/README.md index d5bd8be52b..2737bd5465 100644 --- a/spring-security-rest-full/README.md +++ b/spring-security-rest-full/README.md @@ -8,12 +8,10 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring The "Learn Spring Security" Classes: http://github.learnspringsecurity.com ### Relevant Articles: -- [Spring Security Expressions - hasRole Example](http://www.baeldung.com/spring-security-expressions-basic) - [REST Pagination in Spring](http://www.baeldung.com/2012/01/18/rest-pagination-in-spring/) - [HATEOAS for a Spring REST Service](http://www.baeldung.com/2011/11/13/rest-service-discoverability-with-spring-part-5/) - [REST API Discoverability and HATEOAS](http://www.baeldung.com/2011/11/06/restful-web-service-discoverability-part-4/) - [ETags for REST with Spring](http://www.baeldung.com/2013/01/11/etags-for-rest-with-spring/) -- [Error Handling for REST with Spring 3](http://www.baeldung.com/2013/01/31/exception-handling-for-rest-with-spring-3-2/) - [Integration Testing with the Maven Cargo plugin](http://www.baeldung.com/2011/10/16/how-to-set-up-integration-testing-with-the-maven-cargo-plugin/) - [Introduction to Spring Data JPA](http://www.baeldung.com/2011/12/22/the-persistence-layer-with-spring-data-jpa/) - [Project Configuration with Spring](http://www.baeldung.com/2012/03/12/project-configuration-with-spring/) @@ -24,11 +22,6 @@ The "Learn Spring Security" Classes: http://github.learnspringsecurity.com - [Metrics for your Spring REST API](http://www.baeldung.com/spring-rest-api-metrics) - [REST Query Language with RSQL](http://www.baeldung.com/rest-api-search-language-rsql-fiql) - [Spring RestTemplate Tutorial](http://www.baeldung.com/rest-template) -- [A Guide to CSRF Protection in Spring Security](http://www.baeldung.com/spring-security-csrf) -- [Intro to Spring Security Expressions](http://www.baeldung.com/spring-security-expressions) -- [Changing Spring Model Parameters with Handler Interceptor](http://www.baeldung.com/spring-model-parameters-with-handler-interceptor) -- [Introduction to Spring MVC HandlerInterceptor](http://www.baeldung.com/spring-mvc-handlerinterceptor) -- [Using a Custom Spring MVC’s Handler Interceptor to Manage Sessions](http://www.baeldung.com/spring-mvc-custom-handler-interceptor) - [Bootstrap a Web Application with Spring 4](http://www.baeldung.com/bootstraping-a-web-application-with-spring-and-java-based-configuration) - [REST Query Language – Implementing OR Operation](http://www.baeldung.com/rest-api-query-search-or-operation) diff --git a/spring-security-rest-full/pom.xml b/spring-security-rest-full/pom.xml index 2b559ddefc..12a611431e 100644 --- a/spring-security-rest-full/pom.xml +++ b/spring-security-rest-full/pom.xml @@ -38,17 +38,6 @@ provided - - - - org.springframework.security - spring-security-web - - - org.springframework.security - spring-security-config - - @@ -220,10 +209,6 @@ test - - org.springframework.security - spring-security-test - diff --git a/spring-security-rest-full/src/main/java/org/baeldung/persistence/service/impl/FooService.java b/spring-security-rest-full/src/main/java/org/baeldung/persistence/service/impl/FooService.java index 49f9ec7e97..376082b2d5 100644 --- a/spring-security-rest-full/src/main/java/org/baeldung/persistence/service/impl/FooService.java +++ b/spring-security-rest-full/src/main/java/org/baeldung/persistence/service/impl/FooService.java @@ -10,7 +10,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.repository.PagingAndSortingRepository; -import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -36,6 +35,7 @@ public class FooService extends AbstractService implements IFooService { // custom methods + @Override public Foo retrieveByName(final String name) { return dao.retrieveByName(name); } @@ -44,7 +44,6 @@ public class FooService extends AbstractService implements IFooService { @Override @Transactional(readOnly = true) - @PreAuthorize("hasRole('ROLE_ADMIN')") public List findAll() { return Lists.newArrayList(getDao().findAll()); } diff --git a/spring-security-rest-full/src/main/java/org/baeldung/spring/WebConfig.java b/spring-security-rest-full/src/main/java/org/baeldung/spring/WebConfig.java index efdb2bc8d4..86cb93175a 100644 --- a/spring-security-rest-full/src/main/java/org/baeldung/spring/WebConfig.java +++ b/spring-security-rest-full/src/main/java/org/baeldung/spring/WebConfig.java @@ -1,14 +1,10 @@ package org.baeldung.spring; -import org.baeldung.web.interceptor.LoggerInterceptor; -import org.baeldung.web.interceptor.SessionTimerInterceptor; -import org.baeldung.web.interceptor.UserInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.EnableWebMvc; -import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import org.springframework.web.servlet.view.InternalResourceViewResolver; @@ -35,15 +31,7 @@ public class WebConfig extends WebMvcConfigurerAdapter { public void addViewControllers(final ViewControllerRegistry registry) { super.addViewControllers(registry); registry.addViewController("/graph.html"); - registry.addViewController("/csrfHome.html"); registry.addViewController("/homepage.html"); } - @Override - public void addInterceptors(final InterceptorRegistry registry) { - registry.addInterceptor(new LoggerInterceptor()); - registry.addInterceptor(new UserInterceptor()); - registry.addInterceptor(new SessionTimerInterceptor()); - } - } \ No newline at end of file diff --git a/spring-security-rest-full/src/main/java/org/baeldung/web/controller/RootController.java b/spring-security-rest-full/src/main/java/org/baeldung/web/controller/RootController.java index 8b63275b66..e23da6420d 100644 --- a/spring-security-rest-full/src/main/java/org/baeldung/web/controller/RootController.java +++ b/spring-security-rest-full/src/main/java/org/baeldung/web/controller/RootController.java @@ -11,7 +11,6 @@ import org.baeldung.web.metric.IMetricService; import org.baeldung.web.util.LinkUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; -import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @@ -53,7 +52,6 @@ public class RootController { return metricService.getFullMetric(); } - @PreAuthorize("hasRole('ROLE_ADMIN')") @RequestMapping(value = "/status-metric", method = RequestMethod.GET) @ResponseBody public Map getStatusMetric() { @@ -70,16 +68,5 @@ public class RootController { return result; } - @RequestMapping(value = "/admin/x", method = RequestMethod.GET) - @ResponseBody - public String sampleAdminPage() { - return "Hello"; - } - - @RequestMapping(value = "/my-error-page", method = RequestMethod.GET) - @ResponseBody - public String sampleErrorPage() { - return "Error Occurred"; - } } diff --git a/spring-security-rest-full/src/main/java/org/baeldung/web/error/RestResponseEntityExceptionHandler.java b/spring-security-rest-full/src/main/java/org/baeldung/web/error/RestResponseEntityExceptionHandler.java index e9d34aa9cf..b593116c4a 100644 --- a/spring-security-rest-full/src/main/java/org/baeldung/web/error/RestResponseEntityExceptionHandler.java +++ b/spring-security-rest-full/src/main/java/org/baeldung/web/error/RestResponseEntityExceptionHandler.java @@ -11,13 +11,11 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageNotReadableException; -import org.springframework.security.access.AccessDeniedException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.context.request.WebRequest; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; -//import org.springframework.security.access.AccessDeniedException; @ControllerAdvice public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler { @@ -55,12 +53,6 @@ public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionH return handleExceptionInternal(ex, bodyOfResponse, headers, HttpStatus.BAD_REQUEST, request); } - // 403 - @ExceptionHandler({ AccessDeniedException.class }) - public ResponseEntity handleAccessDeniedException(final Exception ex, final WebRequest request) { - System.out.println("request" + request.getUserPrincipal()); - return new ResponseEntity("Access denied message here", new HttpHeaders(), HttpStatus.FORBIDDEN); - } // 404 diff --git a/spring-security-rest-full/src/main/resources/webSecurityConfig.xml b/spring-security-rest-full/src/main/resources/webSecurityConfig.xml deleted file mode 100644 index be6b4d0c27..0000000000 --- a/spring-security-rest-full/src/main/resources/webSecurityConfig.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/spring-security-rest-full/src/test/java/org/baeldung/TestSuite.java b/spring-security-rest-full/src/test/java/org/baeldung/TestSuite.java index 52e3607b12..cd5fa4661f 100644 --- a/spring-security-rest-full/src/test/java/org/baeldung/TestSuite.java +++ b/spring-security-rest-full/src/test/java/org/baeldung/TestSuite.java @@ -1,7 +1,6 @@ package org.baeldung; import org.baeldung.persistence.PersistenceTestSuite; -import org.baeldung.security.SecurityTestSuite; import org.baeldung.web.LiveTestSuite; import org.junit.runner.RunWith; import org.junit.runners.Suite; @@ -10,7 +9,6 @@ import org.junit.runners.Suite; @Suite.SuiteClasses({ // @formatter:off PersistenceTestSuite.class - ,SecurityTestSuite.class ,LiveTestSuite.class }) // public class TestSuite { diff --git a/spring-security-rest-full/src/test/java/org/baeldung/security/SecurityTestSuite.java b/spring-security-rest-full/src/test/java/org/baeldung/security/SecurityTestSuite.java deleted file mode 100644 index 8b754a03ff..0000000000 --- a/spring-security-rest-full/src/test/java/org/baeldung/security/SecurityTestSuite.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.baeldung.security; - -import org.baeldung.security.csrf.CsrfDisabledIntegrationTest; -import org.baeldung.security.csrf.CsrfEnabledIntegrationTest; -import org.junit.runner.RunWith; -import org.junit.runners.Suite; - -@RunWith(Suite.class) -@Suite.SuiteClasses({ - // @formatter:off - CsrfEnabledIntegrationTest.class - ,CsrfDisabledIntegrationTest.class -}) // -public class SecurityTestSuite { - -} diff --git a/spring-security-rest-full/src/test/java/org/baeldung/security/csrf/CsrfDisabledIntegrationTest.java b/spring-security-rest-full/src/test/java/org/baeldung/security/csrf/CsrfDisabledIntegrationTest.java deleted file mode 100644 index e06461fc2c..0000000000 --- a/spring-security-rest-full/src/test/java/org/baeldung/security/csrf/CsrfDisabledIntegrationTest.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.baeldung.security.csrf; - -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -import org.baeldung.security.spring.SecurityWithoutCsrfConfig; -import org.baeldung.spring.PersistenceConfig; -import org.baeldung.spring.WebConfig; -import org.junit.Test; -import org.springframework.http.MediaType; -import org.springframework.test.context.ContextConfiguration; - -@ContextConfiguration(classes = { SecurityWithoutCsrfConfig.class, PersistenceConfig.class, WebConfig.class }) -public class CsrfDisabledIntegrationTest extends CsrfAbstractIntegrationTest { - - @Test - public void givenNotAuth_whenAddFoo_thenUnauthorized() throws Exception { - mvc.perform(post("/auth/foos").contentType(MediaType.APPLICATION_JSON).content(createFoo())).andExpect(status().isUnauthorized()); - } - - @Test - public void givenAuth_whenAddFoo_thenCreated() throws Exception { - mvc.perform(post("/auth/foos").contentType(MediaType.APPLICATION_JSON).content(createFoo()).with(testUser())).andExpect(status().isCreated()); - } - - @Test - public void accessMainPageWithoutAuthorization() throws Exception { - mvc.perform(get("/graph.html").contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()); - } - - @Test - public void accessOtherPages() throws Exception { - mvc.perform(get("/auth/transfer").contentType(MediaType.APPLICATION_JSON).param("accountNo", "1").param("amount", "100")).andExpect(status().isUnauthorized()); // without authorization - mvc.perform(get("/auth/transfer").contentType(MediaType.APPLICATION_JSON).param("accountNo", "1").param("amount", "100").with(testUser())).andExpect(status().isOk()); // with authorization - } - - @Test - public void accessAdminPage() throws Exception { - mvc.perform(get("/auth/admin/x").contentType(MediaType.APPLICATION_JSON)).andExpect(status().isUnauthorized()); // without authorization - mvc.perform(get("/auth/admin/x").contentType(MediaType.APPLICATION_JSON).with(testAdmin())).andExpect(status().isOk()); // with authorization - } - -} diff --git a/spring-security-rest/README.md b/spring-security-rest/README.md index 92b759a66a..5898ba248e 100644 --- a/spring-security-rest/README.md +++ b/spring-security-rest/README.md @@ -14,3 +14,6 @@ The "Learn Spring Security" Classes: http://github.learnspringsecurity.com - [An Intro to Spring HATEOAS](http://www.baeldung.com/spring-hateoas-tutorial) - [Spring Security Context Propagation with @Async](http://www.baeldung.com/spring-security-async-principal-propagation) - [Servlet 3 Async Support with Spring MVC and Spring Security](http://www.baeldung.com/spring-mvc-async-security) +- [Intro to Spring Security Expressions](http://www.baeldung.com/spring-security-expressions) +- [Spring Security Expressions - hasRole Example](http://www.baeldung.com/spring-security-expressions-basic) +- [Error Handling for REST with Spring 3](http://www.baeldung.com/2013/01/31/exception-handling-for-rest-with-spring-3-2/) diff --git a/spring-security-rest/pom.xml b/spring-security-rest/pom.xml index 13db431ae3..e4bb071a6a 100644 --- a/spring-security-rest/pom.xml +++ b/spring-security-rest/pom.xml @@ -28,7 +28,7 @@ spring-security-config ${org.springframework.security.version} - + diff --git a/spring-security-rest/src/main/java/org/baeldung/spring/SecurityJavaConfig.java b/spring-security-rest/src/main/java/org/baeldung/spring/SecurityJavaConfig.java index 2dbc9b8f08..c3e738297a 100644 --- a/spring-security-rest/src/main/java/org/baeldung/spring/SecurityJavaConfig.java +++ b/spring-security-rest/src/main/java/org/baeldung/spring/SecurityJavaConfig.java @@ -1,10 +1,13 @@ package org.baeldung.spring; import org.baeldung.security.MySavedRequestAwareAuthenticationSuccessHandler; +import org.baeldung.web.error.CustomAccessDeniedHandler; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @@ -13,9 +16,13 @@ import org.springframework.security.web.authentication.SimpleUrlAuthenticationFa @Configuration @EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true) @ComponentScan("org.baeldung.security") public class SecurityJavaConfig extends WebSecurityConfigurerAdapter { + @Autowired + private CustomAccessDeniedHandler accessDeniedHandler; + // @Autowired // private RestAuthenticationEntryPoint restAuthenticationEntryPoint; @@ -40,14 +47,15 @@ public class SecurityJavaConfig extends WebSecurityConfigurerAdapter { .csrf().disable() .authorizeRequests() .and() - .exceptionHandling() -// .authenticationEntryPoint(restAuthenticationEntryPoint) + .exceptionHandling().accessDeniedHandler(accessDeniedHandler) + // .authenticationEntryPoint(restAuthenticationEntryPoint) .and() .authorizeRequests() .antMatchers("/api/csrfAttacker*").permitAll() .antMatchers("/api/customer/**").permitAll() .antMatchers("/api/foos/**").authenticated() .antMatchers("/api/async/**").permitAll() + .antMatchers("/api/admin/**").hasRole("ADMIN") .and() .httpBasic() // .and() diff --git a/spring-security-rest/src/main/java/org/baeldung/spring/SwaggerConfig.java b/spring-security-rest/src/main/java/org/baeldung/spring/SwaggerConfig.java index 00ccb36666..bcf6657eee 100644 --- a/spring-security-rest/src/main/java/org/baeldung/spring/SwaggerConfig.java +++ b/spring-security-rest/src/main/java/org/baeldung/spring/SwaggerConfig.java @@ -1,19 +1,23 @@ package org.baeldung.spring; +import static com.google.common.collect.Lists.newArrayList; + +import java.util.Collections; + import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.bind.annotation.RequestMethod; + import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.builders.ResponseMessageBuilder; import springfox.documentation.schema.ModelRef; import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.Contact; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; -import static com.google.common.collect.Lists.newArrayList; - @Configuration @EnableSwagger2 public class SwaggerConfig { @@ -25,7 +29,7 @@ public class SwaggerConfig { } private ApiInfo apiInfo() { - ApiInfo apiInfo = new ApiInfo("My REST API", "Some custom description of API.", "API TOS", "Terms of service", "myeaddress@company.com", "License of API", "API license URL"); + ApiInfo apiInfo = new ApiInfo("My REST API", "Some custom description of API.", "API TOS", "Terms of service", new Contact("John Doe", "www.example.com", "myeaddress@company.com"), "License of API", "API license URL", Collections.emptyList()); return apiInfo; } } diff --git a/spring-security-rest/src/main/java/org/baeldung/web/controller/RootController.java b/spring-security-rest/src/main/java/org/baeldung/web/controller/RootController.java new file mode 100644 index 0000000000..4253ed7a59 --- /dev/null +++ b/spring-security-rest/src/main/java/org/baeldung/web/controller/RootController.java @@ -0,0 +1,30 @@ +package org.baeldung.web.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +public class RootController { + + public RootController() { + super(); + } + + // API + + @RequestMapping(value = "/admin/x", method = RequestMethod.GET) + @ResponseBody + public String sampleAdminPage() { + return "Hello"; + } + + + @RequestMapping(value = "/my-error-page", method = RequestMethod.GET) + @ResponseBody + public String sampleErrorPage() { + return "Error Occurred"; + } + +} diff --git a/spring-security-rest-full/src/main/java/org/baeldung/web/error/CustomAccessDeniedHandler.java b/spring-security-rest/src/main/java/org/baeldung/web/error/CustomAccessDeniedHandler.java similarity index 100% rename from spring-security-rest-full/src/main/java/org/baeldung/web/error/CustomAccessDeniedHandler.java rename to spring-security-rest/src/main/java/org/baeldung/web/error/CustomAccessDeniedHandler.java diff --git a/spring-security-rest/src/main/java/org/baeldung/web/error/RestResponseEntityExceptionHandler.java b/spring-security-rest/src/main/java/org/baeldung/web/error/RestResponseEntityExceptionHandler.java new file mode 100644 index 0000000000..9ff4f040fe --- /dev/null +++ b/spring-security-rest/src/main/java/org/baeldung/web/error/RestResponseEntityExceptionHandler.java @@ -0,0 +1,74 @@ +package org.baeldung.web.error; + +import org.springframework.dao.DataAccessException; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; +//import org.springframework.security.access.AccessDeniedException; + +@ControllerAdvice +public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler { + + public RestResponseEntityExceptionHandler() { + super(); + } + + // API + + // 400 + + @ExceptionHandler({ DataIntegrityViolationException.class }) + public ResponseEntity handleBadRequest(final DataIntegrityViolationException ex, final WebRequest request) { + final String bodyOfResponse = "This should be application specific"; + return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.BAD_REQUEST, request); + } + + @Override + protected ResponseEntity handleHttpMessageNotReadable(final HttpMessageNotReadableException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) { + final String bodyOfResponse = "This should be application specific"; + // ex.getCause() instanceof JsonMappingException, JsonParseException // for additional information later on + return handleExceptionInternal(ex, bodyOfResponse, headers, HttpStatus.BAD_REQUEST, request); + } + + @Override + protected ResponseEntity handleMethodArgumentNotValid(final MethodArgumentNotValidException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) { + final String bodyOfResponse = "This should be application specific"; + return handleExceptionInternal(ex, bodyOfResponse, headers, HttpStatus.BAD_REQUEST, request); + } + + // 403 + @ExceptionHandler({ AccessDeniedException.class }) + public ResponseEntity handleAccessDeniedException(final Exception ex, final WebRequest request) { + System.out.println("request" + request.getUserPrincipal()); + return new ResponseEntity("Access denied message here", new HttpHeaders(), HttpStatus.FORBIDDEN); + } + + // 409 + + @ExceptionHandler({ InvalidDataAccessApiUsageException.class, DataAccessException.class }) + protected ResponseEntity handleConflict(final RuntimeException ex, final WebRequest request) { + final String bodyOfResponse = "This should be application specific"; + return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.CONFLICT, request); + } + + // 412 + + // 500 + + @ExceptionHandler({ NullPointerException.class, IllegalArgumentException.class, IllegalStateException.class }) + /*500*/public ResponseEntity handleInternal(final RuntimeException ex, final WebRequest request) { + logger.error("500 Status Code", ex); + final String bodyOfResponse = "This should be application specific"; + return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR, request); + } + +} diff --git a/spring-security-rest/src/main/java/org/baeldung/web/exception/MyResourceNotFoundException.java b/spring-security-rest/src/main/java/org/baeldung/web/exception/MyResourceNotFoundException.java new file mode 100644 index 0000000000..14b61f9832 --- /dev/null +++ b/spring-security-rest/src/main/java/org/baeldung/web/exception/MyResourceNotFoundException.java @@ -0,0 +1,21 @@ +package org.baeldung.web.exception; + +public final class MyResourceNotFoundException extends RuntimeException { + + public MyResourceNotFoundException() { + super(); + } + + public MyResourceNotFoundException(final String message, final Throwable cause) { + super(message, cause); + } + + public MyResourceNotFoundException(final String message) { + super(message); + } + + public MyResourceNotFoundException(final Throwable cause) { + super(cause); + } + +} diff --git a/spring-security-rest/src/main/resources/webSecurityConfig.xml b/spring-security-rest/src/main/resources/webSecurityConfig.xml index a260460d76..4bb208a195 100644 --- a/spring-security-rest/src/main/resources/webSecurityConfig.xml +++ b/spring-security-rest/src/main/resources/webSecurityConfig.xml @@ -10,6 +10,8 @@ http://www.springframework.org/schema/beans/spring-beans-4.2.xsd"> + + @@ -17,6 +19,11 @@ + + + + + @@ -30,8 +37,12 @@ + + + + \ No newline at end of file diff --git a/spring-security-rest/src/test/java/org/baeldung/web/FooLiveTest.java b/spring-security-rest/src/test/java/org/baeldung/web/FooLiveTest.java index 0ef50f745a..0a53da674a 100644 --- a/spring-security-rest/src/test/java/org/baeldung/web/FooLiveTest.java +++ b/spring-security-rest/src/test/java/org/baeldung/web/FooLiveTest.java @@ -17,17 +17,20 @@ import com.jayway.restassured.specification.RequestSpecification; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { TestConfig.class }, loader = AnnotationConfigContextLoader.class) public class FooLiveTest { - private static final String URL_PREFIX = "http://localhost:8082/spring-security-rest"; + private static final String URL_PREFIX = "http://localhost:8080/spring-security-rest"; // private FormAuthConfig formConfig = new FormAuthConfig(URL_PREFIX + "/login", "temporary", "temporary"); private String cookie; private RequestSpecification givenAuth() { // return RestAssured.given().auth().form("user", "userPass", formConfig); - if (cookie == null) { - cookie = RestAssured.given().contentType("application/x-www-form-urlencoded").formParam("password", "userPass").formParam("username", "user").post(URL_PREFIX + "/login").getCookie("JSESSIONID"); - } - return RestAssured.given().cookie("JSESSIONID", cookie); + // if (cookie == null) { + // cookie = RestAssured.given().contentType("application/x-www-form-urlencoded").formParam("password", "userPass").formParam("username", "user").post(URL_PREFIX + "/login").getCookie("JSESSIONID"); + // } + // return RestAssured.given().cookie("JSESSIONID", cookie); + return RestAssured.given() + .auth() + .basic("user", "userPass"); } @Test diff --git a/spring-security-rest/src/test/java/org/baeldung/web/SwaggerLiveTest.java b/spring-security-rest/src/test/java/org/baeldung/web/SwaggerLiveTest.java index cf1516f8e1..792b3e28ce 100644 --- a/spring-security-rest/src/test/java/org/baeldung/web/SwaggerLiveTest.java +++ b/spring-security-rest/src/test/java/org/baeldung/web/SwaggerLiveTest.java @@ -8,7 +8,7 @@ import com.jayway.restassured.RestAssured; import com.jayway.restassured.response.Response; public class SwaggerLiveTest { - private static final String URL_PREFIX = "http://localhost:8082/spring-security-rest/api"; + private static final String URL_PREFIX = "http://localhost:8080/spring-security-rest/api"; @Test public void whenVerifySpringFoxIsWorking_thenOK() { diff --git a/vaadin/README.md b/vaadin/README.md index ff12555376..0dfeea8996 100644 --- a/vaadin/README.md +++ b/vaadin/README.md @@ -1 +1,3 @@ ## Relevant articles: + +- [Introduction to Vaadin](http://www.baeldung.com/vaadin)