diff --git a/.gitignore b/.gitignore index 784627b616f9..f3fa30f3e367 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +*/bin/* + *.class # Package Files # diff --git a/.travis.yml b/.travis.yml index c2a369a1b3fe..bcff16f5f171 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,13 @@ language: java -install: travis_wait 40 mvn -q clean install -Dgib.enabled=true +install: travis_wait 60 mvn -q test + +before_script: + - echo "MAVEN_OPTS='-Xmx2048M -Xss128M -XX:MaxPermSize=2048M -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -XX:-UseGCOverheadLimit'" > ~/.mavenrc jdk: - oraclejdk8 -sudo: false addons: apt: packages: @@ -14,4 +16,6 @@ addons: cache: directories: - .autoconf - - $HOME/.m2 \ No newline at end of file + - $HOME/.m2 + + diff --git a/Twitter4J/README.md b/Twitter4J/README.md new file mode 100644 index 000000000000..3057c1c4b2b9 --- /dev/null +++ b/Twitter4J/README.md @@ -0,0 +1,3 @@ +### Relevant articles + +- [Introduction to Twitter4J](http://www.baeldung.com/twitter4j) diff --git a/Twitter4J/pom.xml b/Twitter4J/pom.xml new file mode 100644 index 000000000000..7e41d8de7660 --- /dev/null +++ b/Twitter4J/pom.xml @@ -0,0 +1,53 @@ + + 4.0.0 + com.mabsisa + Twitter4J + jar + 1.0-SNAPSHOT + Twitter4J + http://maven.apache.org + + + UTF-8 + UTF-8 + 1.8 + 1.8 + 1.8 + + + + + org.twitter4j + twitter4j-stream + 4.0.6 + + + junit + junit + 4.12 + + + + + ${project.artifactId} + + + src/main/resources + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.19.1 + + + **/ApplicationTest.java + + + + + + + diff --git a/Twitter4J/src/main/java/com/baeldung/Application.java b/Twitter4J/src/main/java/com/baeldung/Application.java new file mode 100644 index 000000000000..3f961ccb4f59 --- /dev/null +++ b/Twitter4J/src/main/java/com/baeldung/Application.java @@ -0,0 +1,116 @@ +/** + * + */ +package com.baeldung; + +import java.util.List; +import java.util.stream.Collectors; + +import twitter4j.DirectMessage; +import twitter4j.Query; +import twitter4j.QueryResult; +import twitter4j.StallWarning; +import twitter4j.Status; +import twitter4j.StatusDeletionNotice; +import twitter4j.StatusListener; +import twitter4j.Twitter; +import twitter4j.TwitterException; +import twitter4j.TwitterFactory; +import twitter4j.TwitterStream; +import twitter4j.TwitterStreamFactory; +import twitter4j.conf.ConfigurationBuilder; + +public class Application { + + public static Twitter getTwitterinstance() { + /** + * if not using properties file, we can set access token by following way + */ +// ConfigurationBuilder cb = new ConfigurationBuilder(); +// cb.setDebugEnabled(true) +// .setOAuthConsumerKey("//TODO") +// .setOAuthConsumerSecret("//TODO") +// .setOAuthAccessToken("//TODO") +// .setOAuthAccessTokenSecret("//TODO"); +// TwitterFactory tf = new TwitterFactory(cb.build()); +// Twitter twitter = tf.getSingleton(); + + Twitter twitter = TwitterFactory.getSingleton(); + return twitter; + + } + + public static String createTweet(String tweet) throws TwitterException { + Twitter twitter = getTwitterinstance(); + Status status = twitter.updateStatus("creating baeldung API"); + return status.getText(); + } + + public static List getTimeLine() throws TwitterException { + Twitter twitter = getTwitterinstance(); + List statuses = twitter.getHomeTimeline(); + return statuses.stream().map( + item -> item.getText()).collect( + Collectors.toList()); + } + + public static String sendDirectMessage(String recipientName, String msg) throws TwitterException { + Twitter twitter = getTwitterinstance(); + DirectMessage message = twitter.sendDirectMessage(recipientName, msg); + return message.getText(); + } + + public static List searchtweets() throws TwitterException { + Twitter twitter = getTwitterinstance(); + Query query = new Query("source:twitter4j baeldung"); + QueryResult result = twitter.search(query); + List statuses = result.getTweets(); + return statuses.stream().map( + item -> item.getText()).collect( + Collectors.toList()); + } + + public static void streamFeed() { + + StatusListener listener = new StatusListener(){ + + @Override + public void onException(Exception e) { + e.printStackTrace(); + } + + @Override + public void onDeletionNotice(StatusDeletionNotice arg) { + System.out.println("Got a status deletion notice id:" + arg.getStatusId()); + } + + @Override + public void onScrubGeo(long userId, long upToStatusId) { + System.out.println("Got scrub_geo event userId:" + userId + " upToStatusId:" + upToStatusId); + } + + @Override + public void onStallWarning(StallWarning warning) { + System.out.println("Got stall warning:" + warning); + } + + @Override + public void onStatus(Status status) { + System.out.println(status.getUser().getName() + " : " + status.getText()); + } + + @Override + public void onTrackLimitationNotice(int numberOfLimitedStatuses) { + System.out.println("Got track limitation notice:" + numberOfLimitedStatuses); + } + }; + + TwitterStream twitterStream = new TwitterStreamFactory().getInstance(); + + twitterStream.addListener(listener); + + twitterStream.sample(); + + } + +} diff --git a/Twitter4J/src/main/resources/twitter4j.properties b/Twitter4J/src/main/resources/twitter4j.properties new file mode 100644 index 000000000000..ee11dc62a1ce --- /dev/null +++ b/Twitter4J/src/main/resources/twitter4j.properties @@ -0,0 +1,4 @@ +oauth.consumerKey=//TODO +oauth.consumerSecret=//TODO +oauth.accessToken=//TODO +oauth.accessTokenSecret=//TODO diff --git a/Twitter4J/src/test/java/com/baeldung/ApplicationTest.java b/Twitter4J/src/test/java/com/baeldung/ApplicationTest.java new file mode 100644 index 000000000000..a1c778679c7a --- /dev/null +++ b/Twitter4J/src/test/java/com/baeldung/ApplicationTest.java @@ -0,0 +1,40 @@ +package com.baeldung; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import twitter4j.TwitterException; + +public class ApplicationTest { + + /** + * In order run this jUnit test you need to configure your API details in the twitter4j.properties + */ + + String tweet = "baeldung is awsome"; + + @Test + public void givenText_updateStatus() throws TwitterException { + String text = Application.createTweet(tweet); + assertEquals(tweet, text); + } + + @Test + public void givenCredential_fetchStatus() throws TwitterException { + List statuses = Application.getTimeLine(); + List expectedStatuses = new ArrayList(); + expectedStatuses.add(tweet); + assertEquals(expectedStatuses, statuses); + } + + @Test + public void givenRecipientNameAndMessage_sendDirectMessage() throws TwitterException { + String msg = Application.sendDirectMessage("YOUR_RECCIPIENT_ID", tweet); + assertEquals(msg, tweet); + } + +} diff --git a/algorithms/.gitignore b/algorithms/.gitignore new file mode 100644 index 000000000000..b83d22266ac8 --- /dev/null +++ b/algorithms/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/algorithms/README.md b/algorithms/README.md index 42f696d9be14..f1e12ee243a9 100644 --- a/algorithms/README.md +++ b/algorithms/README.md @@ -2,3 +2,7 @@ - [Dijkstra Algorithm in Java](http://www.baeldung.com/java-dijkstra) - [Introduction to Cobertura](http://www.baeldung.com/cobertura) +- [Ant Colony Optimization](http://www.baeldung.com/java-ant-colony-optimization) +- [Validating Input With Finite Automata in Java](http://www.baeldung.com/finite-automata-java) +- [Introduction to Jenetics Library](http://www.baeldung.com/jenetics) +- [Check If a Number Is Prime in Java](http://www.baeldung.com/java-prime-numbers) diff --git a/algorithms/pom.xml b/algorithms/pom.xml index f72457650a9c..884c804d1369 100644 --- a/algorithms/pom.xml +++ b/algorithms/pom.xml @@ -9,15 +9,33 @@ 4.12 3.6.0 1.5.0 + 1.16.12 + 3.6.1 + + org.apache.commons + commons-math3 + ${commons-math3.version} + junit junit ${junit.version} test + + org.projectlombok + lombok + ${lombok.version} + provided + + + io.jenetics + jenetics + 3.7.0 + diff --git a/core-java/src/main/java/com/baeldung/algorithms/RunAlgorithm.java b/algorithms/src/main/java/com/baeldung/algorithms/RunAlgorithm.java similarity index 84% rename from core-java/src/main/java/com/baeldung/algorithms/RunAlgorithm.java rename to algorithms/src/main/java/com/baeldung/algorithms/RunAlgorithm.java index 22c57762935b..6ab7dbb4e51b 100644 --- a/core-java/src/main/java/com/baeldung/algorithms/RunAlgorithm.java +++ b/algorithms/src/main/java/com/baeldung/algorithms/RunAlgorithm.java @@ -9,13 +9,14 @@ public class RunAlgorithm { - public static void main(String[] args) { + public static void main(String[] args) throws InstantiationException, IllegalAccessException { Scanner in = new Scanner(System.in); System.out.println("Run algorithm:"); System.out.println("1 - Simulated Annealing"); System.out.println("2 - Slope One"); System.out.println("3 - Simple Genetic Algorithm"); System.out.println("4 - Ant Colony"); + System.out.println("5 - Dijkstra"); int decision = in.nextInt(); switch (decision) { case 1: @@ -33,6 +34,9 @@ public static void main(String[] args) { AntColonyOptimization antColony = new AntColonyOptimization(21); antColony.startAntOptimization(); break; + case 5: + System.out.println("Please run the DijkstraAlgorithmTest."); + break; default: System.out.println("Unknown option"); break; diff --git a/core-java/src/main/java/com/baeldung/algorithms/ga/annealing/City.java b/algorithms/src/main/java/com/baeldung/algorithms/ga/annealing/City.java similarity index 100% rename from core-java/src/main/java/com/baeldung/algorithms/ga/annealing/City.java rename to algorithms/src/main/java/com/baeldung/algorithms/ga/annealing/City.java diff --git a/core-java/src/main/java/com/baeldung/algorithms/ga/annealing/SimulatedAnnealing.java b/algorithms/src/main/java/com/baeldung/algorithms/ga/annealing/SimulatedAnnealing.java similarity index 100% rename from core-java/src/main/java/com/baeldung/algorithms/ga/annealing/SimulatedAnnealing.java rename to algorithms/src/main/java/com/baeldung/algorithms/ga/annealing/SimulatedAnnealing.java diff --git a/core-java/src/main/java/com/baeldung/algorithms/ga/annealing/Travel.java b/algorithms/src/main/java/com/baeldung/algorithms/ga/annealing/Travel.java similarity index 100% rename from core-java/src/main/java/com/baeldung/algorithms/ga/annealing/Travel.java rename to algorithms/src/main/java/com/baeldung/algorithms/ga/annealing/Travel.java diff --git a/core-java/src/main/java/com/baeldung/algorithms/ga/ant_colony/Ant.java b/algorithms/src/main/java/com/baeldung/algorithms/ga/ant_colony/Ant.java similarity index 100% rename from core-java/src/main/java/com/baeldung/algorithms/ga/ant_colony/Ant.java rename to algorithms/src/main/java/com/baeldung/algorithms/ga/ant_colony/Ant.java diff --git a/algorithms/src/main/java/com/baeldung/algorithms/ga/ant_colony/AntColonyOptimization.java b/algorithms/src/main/java/com/baeldung/algorithms/ga/ant_colony/AntColonyOptimization.java new file mode 100644 index 000000000000..62e124d3f336 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/ga/ant_colony/AntColonyOptimization.java @@ -0,0 +1,203 @@ +package com.baeldung.algorithms.ga.ant_colony; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.OptionalInt; +import java.util.Random; +import java.util.stream.IntStream; + +public class AntColonyOptimization { + + private double c = 1.0; + private double alpha = 1; + private double beta = 5; + private double evaporation = 0.5; + private double Q = 500; + private double antFactor = 0.8; + private double randomFactor = 0.01; + + private int maxIterations = 1000; + + private int numberOfCities; + private int numberOfAnts; + private double graph[][]; + private double trails[][]; + private List ants = new ArrayList<>(); + private Random random = new Random(); + private double probabilities[]; + + private int currentIndex; + + private int[] bestTourOrder; + private double bestTourLength; + + public AntColonyOptimization(int noOfCities) { + graph = generateRandomMatrix(noOfCities); + numberOfCities = graph.length; + numberOfAnts = (int) (numberOfCities * antFactor); + + trails = new double[numberOfCities][numberOfCities]; + probabilities = new double[numberOfCities]; + IntStream.range(0, numberOfAnts) + .forEach(i -> ants.add(new Ant(numberOfCities))); + } + + /** + * Generate initial solution + */ + public double[][] generateRandomMatrix(int n) { + double[][] randomMatrix = new double[n][n]; + IntStream.range(0, n) + .forEach(i -> IntStream.range(0, n) + .forEach(j -> randomMatrix[i][j] = Math.abs(random.nextInt(100) + 1))); + return randomMatrix; + } + + /** + * Perform ant optimization + */ + public void startAntOptimization() { + IntStream.rangeClosed(1, 3) + .forEach(i -> { + System.out.println("Attempt #" + i); + solve(); + }); + } + + /** + * Use this method to run the main logic + */ + public int[] solve() { + setupAnts(); + clearTrails(); + IntStream.range(0, maxIterations) + .forEach(i -> { + moveAnts(); + updateTrails(); + updateBest(); + }); + System.out.println("Best tour length: " + (bestTourLength - numberOfCities)); + System.out.println("Best tour order: " + Arrays.toString(bestTourOrder)); + return bestTourOrder.clone(); + } + + /** + * Prepare ants for the simulation + */ + private void setupAnts() { + IntStream.range(0, numberOfAnts) + .forEach(i -> { + ants.forEach(ant -> { + ant.clear(); + ant.visitCity(-1, random.nextInt(numberOfCities)); + }); + }); + currentIndex = 0; + } + + /** + * At each iteration, move ants + */ + private void moveAnts() { + IntStream.range(currentIndex, numberOfCities - 1) + .forEach(i -> { + ants.forEach(ant -> ant.visitCity(currentIndex, selectNextCity(ant))); + currentIndex++; + }); + } + + /** + * Select next city for each ant + */ + private int selectNextCity(Ant ant) { + int t = random.nextInt(numberOfCities - currentIndex); + if (random.nextDouble() < randomFactor) { + OptionalInt cityIndex = IntStream.range(0, numberOfCities) + .filter(i -> i == t && !ant.visited(i)) + .findFirst(); + if (cityIndex.isPresent()) { + return cityIndex.getAsInt(); + } + } + calculateProbabilities(ant); + double r = random.nextDouble(); + double total = 0; + for (int i = 0; i < numberOfCities; i++) { + total += probabilities[i]; + if (total >= r) { + return i; + } + } + + throw new RuntimeException("There are no other cities"); + } + + /** + * Calculate the next city picks probabilites + */ + public void calculateProbabilities(Ant ant) { + int i = ant.trail[currentIndex]; + double pheromone = 0.0; + for (int l = 0; l < numberOfCities; l++) { + if (!ant.visited(l)) { + pheromone += Math.pow(trails[i][l], alpha) * Math.pow(1.0 / graph[i][l], beta); + } + } + for (int j = 0; j < numberOfCities; j++) { + if (ant.visited(j)) { + probabilities[j] = 0.0; + } else { + double numerator = Math.pow(trails[i][j], alpha) * Math.pow(1.0 / graph[i][j], beta); + probabilities[j] = numerator / pheromone; + } + } + } + + /** + * Update trails that ants used + */ + private void updateTrails() { + for (int i = 0; i < numberOfCities; i++) { + for (int j = 0; j < numberOfCities; j++) { + trails[i][j] *= evaporation; + } + } + for (Ant a : ants) { + double contribution = Q / a.trailLength(graph); + for (int i = 0; i < numberOfCities - 1; i++) { + trails[a.trail[i]][a.trail[i + 1]] += contribution; + } + trails[a.trail[numberOfCities - 1]][a.trail[0]] += contribution; + } + } + + /** + * Update the best solution + */ + private void updateBest() { + if (bestTourOrder == null) { + bestTourOrder = ants.get(0).trail; + bestTourLength = ants.get(0) + .trailLength(graph); + } + for (Ant a : ants) { + if (a.trailLength(graph) < bestTourLength) { + bestTourLength = a.trailLength(graph); + bestTourOrder = a.trail.clone(); + } + } + } + + /** + * Clear trails after simulation + */ + private void clearTrails() { + IntStream.range(0, numberOfCities) + .forEach(i -> { + IntStream.range(0, numberOfCities) + .forEach(j -> trails[i][j] = c); + }); + } + +} diff --git a/core-java/src/main/java/com/baeldung/algorithms/ga/binary/Individual.java b/algorithms/src/main/java/com/baeldung/algorithms/ga/binary/Individual.java similarity index 100% rename from core-java/src/main/java/com/baeldung/algorithms/ga/binary/Individual.java rename to algorithms/src/main/java/com/baeldung/algorithms/ga/binary/Individual.java diff --git a/core-java/src/main/java/com/baeldung/algorithms/ga/binary/Population.java b/algorithms/src/main/java/com/baeldung/algorithms/ga/binary/Population.java similarity index 100% rename from core-java/src/main/java/com/baeldung/algorithms/ga/binary/Population.java rename to algorithms/src/main/java/com/baeldung/algorithms/ga/binary/Population.java diff --git a/core-java/src/main/java/com/baeldung/algorithms/ga/binary/SimpleGeneticAlgorithm.java b/algorithms/src/main/java/com/baeldung/algorithms/ga/binary/SimpleGeneticAlgorithm.java similarity index 100% rename from core-java/src/main/java/com/baeldung/algorithms/ga/binary/SimpleGeneticAlgorithm.java rename to algorithms/src/main/java/com/baeldung/algorithms/ga/binary/SimpleGeneticAlgorithm.java diff --git a/algorithms/src/main/java/com/baeldung/algorithms/dijkstra/Dijkstra.java b/algorithms/src/main/java/com/baeldung/algorithms/ga/dijkstra/Dijkstra.java similarity index 95% rename from algorithms/src/main/java/com/baeldung/algorithms/dijkstra/Dijkstra.java rename to algorithms/src/main/java/com/baeldung/algorithms/ga/dijkstra/Dijkstra.java index 1d41f46adb82..0b01e9b48bf7 100644 --- a/algorithms/src/main/java/com/baeldung/algorithms/dijkstra/Dijkstra.java +++ b/algorithms/src/main/java/com/baeldung/algorithms/ga/dijkstra/Dijkstra.java @@ -1,57 +1,57 @@ -package com.baeldung.algorithms.dijkstra; - -import java.util.HashSet; -import java.util.LinkedList; -import java.util.Map.Entry; -import java.util.Set; - -public class Dijkstra { - - public static Graph calculateShortestPathFromSource(Graph graph, Node source) { - - source.setDistance(0); - - Set settledNodes = new HashSet<>(); - Set unsettledNodes = new HashSet<>(); - unsettledNodes.add(source); - - while (unsettledNodes.size() != 0) { - Node currentNode = getLowestDistanceNode(unsettledNodes); - unsettledNodes.remove(currentNode); - for (Entry adjacencyPair : currentNode.getAdjacentNodes().entrySet()) { - Node adjacentNode = adjacencyPair.getKey(); - Integer edgeWeigh = adjacencyPair.getValue(); - - if (!settledNodes.contains(adjacentNode)) { - CalculateMinimumDistance(adjacentNode, edgeWeigh, currentNode); - unsettledNodes.add(adjacentNode); - } - } - settledNodes.add(currentNode); - } - return graph; - } - - private static void CalculateMinimumDistance(Node evaluationNode, Integer edgeWeigh, Node sourceNode) { - Integer sourceDistance = sourceNode.getDistance(); - if (sourceDistance + edgeWeigh < evaluationNode.getDistance()) { - evaluationNode.setDistance(sourceDistance + edgeWeigh); - LinkedList shortestPath = new LinkedList<>(sourceNode.getShortestPath()); - shortestPath.add(sourceNode); - evaluationNode.setShortestPath(shortestPath); - } - } - - private static Node getLowestDistanceNode(Set unsettledNodes) { - Node lowestDistanceNode = null; - int lowestDistance = Integer.MAX_VALUE; - for (Node node : unsettledNodes) { - int nodeDistance = node.getDistance(); - if (nodeDistance < lowestDistance) { - lowestDistance = nodeDistance; - lowestDistanceNode = node; - } - } - return lowestDistanceNode; - } -} +package com.baeldung.algorithms.ga.dijkstra; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Map.Entry; +import java.util.Set; + +public class Dijkstra { + + public static Graph calculateShortestPathFromSource(Graph graph, Node source) { + + source.setDistance(0); + + Set settledNodes = new HashSet<>(); + Set unsettledNodes = new HashSet<>(); + unsettledNodes.add(source); + + while (unsettledNodes.size() != 0) { + Node currentNode = getLowestDistanceNode(unsettledNodes); + unsettledNodes.remove(currentNode); + for (Entry adjacencyPair : currentNode.getAdjacentNodes().entrySet()) { + Node adjacentNode = adjacencyPair.getKey(); + Integer edgeWeigh = adjacencyPair.getValue(); + + if (!settledNodes.contains(adjacentNode)) { + CalculateMinimumDistance(adjacentNode, edgeWeigh, currentNode); + unsettledNodes.add(adjacentNode); + } + } + settledNodes.add(currentNode); + } + return graph; + } + + private static void CalculateMinimumDistance(Node evaluationNode, Integer edgeWeigh, Node sourceNode) { + Integer sourceDistance = sourceNode.getDistance(); + if (sourceDistance + edgeWeigh < evaluationNode.getDistance()) { + evaluationNode.setDistance(sourceDistance + edgeWeigh); + LinkedList shortestPath = new LinkedList<>(sourceNode.getShortestPath()); + shortestPath.add(sourceNode); + evaluationNode.setShortestPath(shortestPath); + } + } + + private static Node getLowestDistanceNode(Set unsettledNodes) { + Node lowestDistanceNode = null; + int lowestDistance = Integer.MAX_VALUE; + for (Node node : unsettledNodes) { + int nodeDistance = node.getDistance(); + if (nodeDistance < lowestDistance) { + lowestDistance = nodeDistance; + lowestDistanceNode = node; + } + } + return lowestDistanceNode; + } +} diff --git a/algorithms/src/main/java/com/baeldung/algorithms/dijkstra/Graph.java b/algorithms/src/main/java/com/baeldung/algorithms/ga/dijkstra/Graph.java similarity index 84% rename from algorithms/src/main/java/com/baeldung/algorithms/dijkstra/Graph.java rename to algorithms/src/main/java/com/baeldung/algorithms/ga/dijkstra/Graph.java index f24d6ae60e32..76694ed76e1f 100644 --- a/algorithms/src/main/java/com/baeldung/algorithms/dijkstra/Graph.java +++ b/algorithms/src/main/java/com/baeldung/algorithms/ga/dijkstra/Graph.java @@ -1,21 +1,21 @@ -package com.baeldung.algorithms.dijkstra; - -import java.util.HashSet; -import java.util.Set; - -public class Graph { - - private Set nodes = new HashSet<>(); - - public void addNode(Node nodeA) { - nodes.add(nodeA); - } - - public Set getNodes() { - return nodes; - } - - public void setNodes(Set nodes) { - this.nodes = nodes; - } -} +package com.baeldung.algorithms.ga.dijkstra; + +import java.util.HashSet; +import java.util.Set; + +public class Graph { + + private Set nodes = new HashSet<>(); + + public void addNode(Node nodeA) { + nodes.add(nodeA); + } + + public Set getNodes() { + return nodes; + } + + public void setNodes(Set nodes) { + this.nodes = nodes; + } +} diff --git a/algorithms/src/main/java/com/baeldung/algorithms/dijkstra/Node.java b/algorithms/src/main/java/com/baeldung/algorithms/ga/dijkstra/Node.java similarity index 92% rename from algorithms/src/main/java/com/baeldung/algorithms/dijkstra/Node.java rename to algorithms/src/main/java/com/baeldung/algorithms/ga/dijkstra/Node.java index b00127a25939..ac34bfadd109 100644 --- a/algorithms/src/main/java/com/baeldung/algorithms/dijkstra/Node.java +++ b/algorithms/src/main/java/com/baeldung/algorithms/ga/dijkstra/Node.java @@ -1,58 +1,58 @@ -package com.baeldung.algorithms.dijkstra; - -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -public class Node { - - private String name; - - private LinkedList shortestPath = new LinkedList<>(); - - private Integer distance = Integer.MAX_VALUE; - - private Map adjacentNodes = new HashMap<>(); - - public Node(String name) { - this.name = name; - } - - public void addDestination(Node destination, int distance) { - adjacentNodes.put(destination, distance); - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Map getAdjacentNodes() { - return adjacentNodes; - } - - public void setAdjacentNodes(Map adjacentNodes) { - this.adjacentNodes = adjacentNodes; - } - - public Integer getDistance() { - return distance; - } - - public void setDistance(Integer distance) { - this.distance = distance; - } - - public List getShortestPath() { - return shortestPath; - } - - public void setShortestPath(LinkedList shortestPath) { - this.shortestPath = shortestPath; - } - -} +package com.baeldung.algorithms.ga.dijkstra; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +public class Node { + + private String name; + + private LinkedList shortestPath = new LinkedList<>(); + + private Integer distance = Integer.MAX_VALUE; + + private Map adjacentNodes = new HashMap<>(); + + public Node(String name) { + this.name = name; + } + + public void addDestination(Node destination, int distance) { + adjacentNodes.put(destination, distance); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Map getAdjacentNodes() { + return adjacentNodes; + } + + public void setAdjacentNodes(Map adjacentNodes) { + this.adjacentNodes = adjacentNodes; + } + + public Integer getDistance() { + return distance; + } + + public void setDistance(Integer distance) { + this.distance = distance; + } + + public List getShortestPath() { + return shortestPath; + } + + public void setShortestPath(LinkedList shortestPath) { + this.shortestPath = shortestPath; + } + +} diff --git a/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/Knapsack.java b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/Knapsack.java new file mode 100644 index 000000000000..cc99ccf20483 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/Knapsack.java @@ -0,0 +1,47 @@ +package com.baeldung.algorithms.ga.jenetics; + +import static org.jenetics.engine.EvolutionResult.toBestPhenotype; +import static org.jenetics.engine.limit.bySteadyFitness; + +import java.util.stream.Stream; + +import org.jenetics.BitChromosome; +import org.jenetics.BitGene; +import org.jenetics.Mutator; +import org.jenetics.Phenotype; +import org.jenetics.RouletteWheelSelector; +import org.jenetics.SinglePointCrossover; +import org.jenetics.TournamentSelector; +import org.jenetics.engine.Engine; +import org.jenetics.engine.EvolutionStatistics; + +//The main class. +public class Knapsack { + + public static void main(String[] args) { + int nItems = 15; + double ksSize = nItems * 100.0 / 3.0; + + KnapsackFF ff = new KnapsackFF(Stream.generate(KnapsackItem::random) + .limit(nItems) + .toArray(KnapsackItem[]::new), ksSize); + + Engine engine = Engine.builder(ff, BitChromosome.of(nItems, 0.5)) + .populationSize(500) + .survivorsSelector(new TournamentSelector<>(5)) + .offspringSelector(new RouletteWheelSelector<>()) + .alterers(new Mutator<>(0.115), new SinglePointCrossover<>(0.16)) + .build(); + + EvolutionStatistics statistics = EvolutionStatistics.ofNumber(); + + Phenotype best = engine.stream() + .limit(bySteadyFitness(7)) + .limit(100) + .peek(statistics) + .collect(toBestPhenotype()); + + System.out.println(statistics); + System.out.println(best); + } +} \ No newline at end of file diff --git a/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/KnapsackFF.java b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/KnapsackFF.java new file mode 100644 index 000000000000..e3e06d301a49 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/KnapsackFF.java @@ -0,0 +1,25 @@ +package com.baeldung.algorithms.ga.jenetics; + +import java.util.function.Function; + +import org.jenetics.BitChromosome; +import org.jenetics.BitGene; +import org.jenetics.Genotype; + +public class KnapsackFF implements Function, Double> { + private KnapsackItem[] items; + private double size; + + public KnapsackFF(KnapsackItem[] items, double size) { + this.items = items; + this.size = size; + } + + @Override + public Double apply(Genotype gt) { + KnapsackItem sum = ((BitChromosome) gt.getChromosome()).ones() + .mapToObj(i -> items[i]) + .collect(KnapsackItem.toSum()); + return sum.size <= this.size ? sum.value : 0; + } +} diff --git a/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/KnapsackItem.java b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/KnapsackItem.java new file mode 100644 index 000000000000..876df0ba2582 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/KnapsackItem.java @@ -0,0 +1,34 @@ +package com.baeldung.algorithms.ga.jenetics; + +import java.util.Random; +import java.util.stream.Collector; + +import org.jenetics.util.RandomRegistry; + +public class KnapsackItem { + + public double size; + public double value; + + public KnapsackItem(double size, double value) { + this.size = size; + this.value = value; + } + + protected static KnapsackItem random() { + Random r = RandomRegistry.getRandom(); + return new KnapsackItem(r.nextDouble() * 100, r.nextDouble() * 100); + } + + protected static Collector toSum() { + return Collector.of(() -> new double[2], (a, b) -> { + a[0] += b.size; + a[1] += b.value; + } , (a, b) -> { + a[0] += b[0]; + a[1] += b[1]; + return a; + } , r -> new KnapsackItem(r[0], r[1])); + } + +} diff --git a/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/SimpleGeneticAlgorithm.java b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/SimpleGeneticAlgorithm.java new file mode 100644 index 000000000000..845e11b3499d --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/SimpleGeneticAlgorithm.java @@ -0,0 +1,33 @@ +package com.baeldung.algorithms.ga.jenetics; + +import org.jenetics.BitChromosome; +import org.jenetics.BitGene; +import org.jenetics.Genotype; +import org.jenetics.engine.Engine; +import org.jenetics.engine.EvolutionResult; +import org.jenetics.util.Factory; + +public class SimpleGeneticAlgorithm { + + private static Integer eval(Genotype gt) { + return gt.getChromosome() + .as(BitChromosome.class) + .bitCount(); + } + + public static void main(String[] args) { + Factory> gtf = Genotype.of(BitChromosome.of(10, 0.5)); + System.out.println("Before the evolution:\n" + gtf); + + Engine engine = Engine.builder(SimpleGeneticAlgorithm::eval, gtf) + .build(); + + Genotype result = engine.stream() + .limit(500) + .collect(EvolutionResult.toBestGenotype()); + + System.out.println("After the evolution:\n" + result); + + } + +} diff --git a/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/SpringsteenProblem.java b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/SpringsteenProblem.java new file mode 100644 index 000000000000..55f2f7af0a30 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/SpringsteenProblem.java @@ -0,0 +1,86 @@ +package com.baeldung.algorithms.ga.jenetics; + +import static java.util.Objects.requireNonNull; + +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.jenetics.BitGene; +import org.jenetics.engine.Codec; +import org.jenetics.engine.Engine; +import org.jenetics.engine.EvolutionResult; +import org.jenetics.engine.Problem; +import org.jenetics.engine.codecs; +import org.jenetics.util.ISeq; + +public class SpringsteenProblem implements Problem, BitGene, Double> { + + private ISeq records; + private double maxPricePerUniqueSong; + + public SpringsteenProblem(ISeq records, double maxPricePerUniqueSong) { + this.records = requireNonNull(records); + this.maxPricePerUniqueSong = maxPricePerUniqueSong; + } + + @Override + public Function, Double> fitness() { + return SpringsteenRecords -> { + double cost = SpringsteenRecords.stream() + .mapToDouble(r -> r.price) + .sum(); + + int uniqueSongCount = SpringsteenRecords.stream() + .flatMap(r -> r.songs.stream()) + .collect(Collectors.toSet()) + .size(); + + double pricePerUniqueSong = cost / uniqueSongCount; + + return pricePerUniqueSong <= maxPricePerUniqueSong ? uniqueSongCount : 0.0; + }; + } + + @Override + public Codec, BitGene> codec() { + return codecs.ofSubSet(records); + } + + public static void main(String[] args) { + double maxPricePerUniqueSong = 2.5; + + SpringsteenProblem springsteen = new SpringsteenProblem( + ISeq.of(new SpringsteenRecord("SpringsteenRecord1", 25, ISeq.of("Song1", "Song2", "Song3", "Song4", "Song5", "Song6")), new SpringsteenRecord("SpringsteenRecord2", 15, ISeq.of("Song2", "Song3", "Song4", "Song5", "Song6", "Song7")), + new SpringsteenRecord("SpringsteenRecord3", 35, ISeq.of("Song5", "Song6", "Song7", "Song8", "Song9", "Song10")), new SpringsteenRecord("SpringsteenRecord4", 17, ISeq.of("Song9", "Song10", "Song12", "Song4", "Song13", "Song14")), + new SpringsteenRecord("SpringsteenRecord5", 29, ISeq.of("Song1", "Song2", "Song13", "Song14", "Song15", "Song16")), new SpringsteenRecord("SpringsteenRecord6", 5, ISeq.of("Song18", "Song20", "Song30", "Song40"))), + maxPricePerUniqueSong); + + Engine engine = Engine.builder(springsteen) + .build(); + + ISeq result = springsteen.codec() + .decoder() + .apply(engine.stream() + .limit(10) + .collect(EvolutionResult.toBestGenotype())); + + double cost = result.stream() + .mapToDouble(r -> r.price) + .sum(); + + int uniqueSongCount = result.stream() + .flatMap(r -> r.songs.stream()) + .collect(Collectors.toSet()) + .size(); + + double pricePerUniqueSong = cost / uniqueSongCount; + + System.out.println("Overall cost: " + cost); + System.out.println("Unique songs: " + uniqueSongCount); + System.out.println("Cost per song: " + pricePerUniqueSong); + System.out.println("Records: " + result.map(r -> r.name) + .toString(", ")); + + } + +} \ No newline at end of file diff --git a/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/SpringsteenRecord.java b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/SpringsteenRecord.java new file mode 100644 index 000000000000..b49709e7f572 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/SpringsteenRecord.java @@ -0,0 +1,24 @@ +package com.baeldung.algorithms.ga.jenetics; + +import static java.util.Objects.requireNonNull; + +import org.jenetics.util.ISeq; + +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class SpringsteenRecord { + + String name; + double price; + ISeq songs; + + public SpringsteenRecord(String name, double price, ISeq songs) { + this.name = requireNonNull(name); + this.price = price; + this.songs = requireNonNull(songs); + } + +} diff --git a/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/SubsetSum.java b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/SubsetSum.java new file mode 100644 index 000000000000..db1e11239f3c --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/SubsetSum.java @@ -0,0 +1,66 @@ +package com.baeldung.algorithms.ga.jenetics; + +import static java.util.Objects.requireNonNull; + +import java.util.Random; +import java.util.function.Function; + +import org.jenetics.EnumGene; +import org.jenetics.Mutator; +import org.jenetics.PartiallyMatchedCrossover; +import org.jenetics.Phenotype; +import org.jenetics.engine.Codec; +import org.jenetics.engine.Engine; +import org.jenetics.engine.EvolutionResult; +import org.jenetics.engine.Problem; +import org.jenetics.engine.codecs; +import org.jenetics.engine.limit; +import org.jenetics.util.ISeq; +import org.jenetics.util.LCG64ShiftRandom; + +public class SubsetSum implements Problem, EnumGene, Integer> { + + private ISeq basicSet; + private int size; + + public SubsetSum(ISeq basicSet, int size) { + this.basicSet = requireNonNull(basicSet); + this.size = size; + } + + @Override + public Function, Integer> fitness() { + return subset -> Math.abs(subset.stream() + .mapToInt(Integer::intValue) + .sum()); + } + + @Override + public Codec, EnumGene> codec() { + return codecs.ofSubSet(basicSet, size); + } + + public static SubsetSum of(int n, int k, Random random) { + return new SubsetSum(random.doubles() + .limit(n) + .mapToObj(d -> (int) ((d - 0.5) * n)) + .collect(ISeq.toISeq()), k); + } + + public static void main(String[] args) { + SubsetSum problem = of(500, 15, new LCG64ShiftRandom(101010)); + + Engine, Integer> engine = Engine.builder(problem) + .minimizing() + .maximalPhenotypeAge(5) + .alterers(new PartiallyMatchedCrossover<>(0.4), new Mutator<>(0.3)) + .build(); + + Phenotype, Integer> result = engine.stream() + .limit(limit.bySteadyFitness(55)) + .collect(EvolutionResult.toBestPhenotype()); + + System.out.print(result); + } + +} \ No newline at end of file diff --git a/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/TravelingSalesman.java b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/TravelingSalesman.java new file mode 100644 index 000000000000..80ede0f8c52c --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/TravelingSalesman.java @@ -0,0 +1,67 @@ +package com.baeldung.algorithms.ga.jenetics; + +import static java.lang.Math.PI; +import static java.lang.Math.abs; +import static java.lang.Math.sin; +import static org.jenetics.engine.EvolutionResult.toBestPhenotype; +import static org.jenetics.engine.limit.bySteadyFitness; + +import java.util.stream.IntStream; + +import org.jenetics.EnumGene; +import org.jenetics.Optimize; +import org.jenetics.PartiallyMatchedCrossover; +import org.jenetics.Phenotype; +import org.jenetics.SwapMutator; +import org.jenetics.engine.Engine; +import org.jenetics.engine.EvolutionStatistics; +import org.jenetics.engine.codecs; + +public class TravelingSalesman { + + private static final int STOPS = 50; + private static final double[][] ADJACENCE = matrix(STOPS); + + private static double[][] matrix(int stops) { + final double radius = 100.0; + double[][] matrix = new double[stops][stops]; + + for (int i = 0; i < stops; ++i) { + for (int j = 0; j < stops; ++j) { + matrix[i][j] = chord(stops, abs(i - j), radius); + } + } + return matrix; + } + + private static double chord(int stops, int i, double r) { + return 2.0 * r * abs(sin(PI * i / stops)); + } + + private static double dist(final int[] path) { + return IntStream.range(0, STOPS) + .mapToDouble(i -> ADJACENCE[path[i]][path[(i + 1) % STOPS]]) + .sum(); + } + + public static void main(String[] args) { + final Engine, Double> engine = Engine.builder(TravelingSalesman::dist, codecs.ofPermutation(STOPS)) + .optimize(Optimize.MINIMUM) + .maximalPhenotypeAge(11) + .populationSize(500) + .alterers(new SwapMutator<>(0.2), new PartiallyMatchedCrossover<>(0.35)) + .build(); + + final EvolutionStatistics statistics = EvolutionStatistics.ofNumber(); + + final Phenotype, Double> best = engine.stream() + .limit(bySteadyFitness(15)) + .limit(250) + .peek(statistics) + .collect(toBestPhenotype()); + + System.out.println(statistics); + System.out.println(best); + } + +} diff --git a/algorithms/src/main/java/com/baeldung/algorithms/primechecker/BigIntegerPrimeChecker.java b/algorithms/src/main/java/com/baeldung/algorithms/primechecker/BigIntegerPrimeChecker.java new file mode 100644 index 000000000000..752e659fa3ea --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/primechecker/BigIntegerPrimeChecker.java @@ -0,0 +1,13 @@ +package com.baeldung.algorithms.primechecker; + +import java.math.BigInteger; + +public class BigIntegerPrimeChecker implements PrimeChecker{ + + @Override + public boolean isPrime(Long number) { + BigInteger bigInt = BigInteger.valueOf(number); + return bigInt.isProbablePrime(100); + } + +} diff --git a/algorithms/src/main/java/com/baeldung/algorithms/primechecker/BruteForcePrimeChecker.java b/algorithms/src/main/java/com/baeldung/algorithms/primechecker/BruteForcePrimeChecker.java new file mode 100644 index 000000000000..47ffb3e22460 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/primechecker/BruteForcePrimeChecker.java @@ -0,0 +1,16 @@ +package com.baeldung.algorithms.primechecker; + +import java.util.stream.IntStream; +import java.util.stream.LongStream; + +public class BruteForcePrimeChecker implements PrimeChecker{ + + @Override + public boolean isPrime(Integer number) { + + return number > 2 ? IntStream.range(2, number) + .noneMatch(n -> (number % n == 0)) : false; + } + + +} diff --git a/algorithms/src/main/java/com/baeldung/algorithms/primechecker/OptimisedPrimeChecker.java b/algorithms/src/main/java/com/baeldung/algorithms/primechecker/OptimisedPrimeChecker.java new file mode 100644 index 000000000000..06ae4acc7f8f --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/primechecker/OptimisedPrimeChecker.java @@ -0,0 +1,15 @@ +package com.baeldung.algorithms.primechecker; + +import java.util.stream.IntStream; +import java.util.stream.LongStream; + +public class OptimisedPrimeChecker implements PrimeChecker{ + + @Override + public boolean isPrime(Integer number) { + return number > 2 ? IntStream.rangeClosed(2, (int) Math.sqrt(number)) + .noneMatch(n -> (number % n == 0)) : false; + } + + +} diff --git a/algorithms/src/main/java/com/baeldung/algorithms/primechecker/PrimeChecker.java b/algorithms/src/main/java/com/baeldung/algorithms/primechecker/PrimeChecker.java new file mode 100644 index 000000000000..5f7a15a939b0 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/primechecker/PrimeChecker.java @@ -0,0 +1,6 @@ +package com.baeldung.algorithms.primechecker; + +public interface PrimeChecker { + + public boolean isPrime( T number ); +} diff --git a/algorithms/src/main/java/com/baeldung/algorithms/primechecker/PrimesPrimeChecker.java b/algorithms/src/main/java/com/baeldung/algorithms/primechecker/PrimesPrimeChecker.java new file mode 100644 index 000000000000..08b095cb7903 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/primechecker/PrimesPrimeChecker.java @@ -0,0 +1,12 @@ +package com.baeldung.algorithms.primechecker; + +import org.apache.commons.math3.primes.Primes; + +public class PrimesPrimeChecker implements PrimeChecker{ + + @Override + public boolean isPrime(Integer number) { + return Primes.isPrime(number); + } + +} diff --git a/core-java/src/main/java/com/baeldung/algorithms/slope_one/InputData.java b/algorithms/src/main/java/com/baeldung/algorithms/slope_one/InputData.java similarity index 100% rename from core-java/src/main/java/com/baeldung/algorithms/slope_one/InputData.java rename to algorithms/src/main/java/com/baeldung/algorithms/slope_one/InputData.java diff --git a/core-java/src/main/java/com/baeldung/algorithms/slope_one/Item.java b/algorithms/src/main/java/com/baeldung/algorithms/slope_one/Item.java similarity index 100% rename from core-java/src/main/java/com/baeldung/algorithms/slope_one/Item.java rename to algorithms/src/main/java/com/baeldung/algorithms/slope_one/Item.java diff --git a/core-java/src/main/java/com/baeldung/algorithms/slope_one/SlopeOne.java b/algorithms/src/main/java/com/baeldung/algorithms/slope_one/SlopeOne.java similarity index 100% rename from core-java/src/main/java/com/baeldung/algorithms/slope_one/SlopeOne.java rename to algorithms/src/main/java/com/baeldung/algorithms/slope_one/SlopeOne.java diff --git a/core-java/src/main/java/com/baeldung/algorithms/slope_one/User.java b/algorithms/src/main/java/com/baeldung/algorithms/slope_one/User.java similarity index 100% rename from core-java/src/main/java/com/baeldung/algorithms/slope_one/User.java rename to algorithms/src/main/java/com/baeldung/algorithms/slope_one/User.java diff --git a/algorithms/src/main/java/com/baeldung/automata/FiniteStateMachine.java b/algorithms/src/main/java/com/baeldung/automata/FiniteStateMachine.java new file mode 100644 index 000000000000..943b44fe057d --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/automata/FiniteStateMachine.java @@ -0,0 +1,20 @@ +package com.baeldung.automata; + +/** + * Finite state machine. + */ +public interface FiniteStateMachine { + + /** + * Follow a transition, switch the state of the machine. + * @param c Char. + * @return A new finite state machine with the new state. + */ + FiniteStateMachine switchState(final CharSequence c); + + /** + * Is the current state a final one? + * @return true or false. + */ + boolean canStop(); +} diff --git a/algorithms/src/main/java/com/baeldung/automata/RtFiniteStateMachine.java b/algorithms/src/main/java/com/baeldung/automata/RtFiniteStateMachine.java new file mode 100644 index 000000000000..090e00c73c2a --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/automata/RtFiniteStateMachine.java @@ -0,0 +1,30 @@ +package com.baeldung.automata; + +/** + * Default implementation of a finite state machine. + * This class is immutable and thread-safe. + */ +public final class RtFiniteStateMachine implements FiniteStateMachine { + + /** + * Current state. + */ + private State current; + + /** + * Ctor. + * @param initial Initial state of this machine. + */ + public RtFiniteStateMachine(final State initial) { + this.current = initial; + } + + public FiniteStateMachine switchState(final CharSequence c) { + return new RtFiniteStateMachine(this.current.transit(c)); + } + + public boolean canStop() { + return this.current.isFinal(); + } + +} diff --git a/algorithms/src/main/java/com/baeldung/automata/RtState.java b/algorithms/src/main/java/com/baeldung/automata/RtState.java new file mode 100644 index 000000000000..b4a5df79612c --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/automata/RtState.java @@ -0,0 +1,42 @@ +package com.baeldung.automata; + +import java.util.ArrayList; +import java.util.List; + +/** + * State in a finite state machine. + */ +public final class RtState implements State { + + private List transitions; + private boolean isFinal; + + public RtState() { + this(false); + } + + public RtState(final boolean isFinal) { + this.transitions = new ArrayList<>(); + this.isFinal = isFinal; + } + + public State transit(final CharSequence c) { + return transitions + .stream() + .filter(t -> t.isPossible(c)) + .map(Transition::state) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("Input not accepted: " + c)); + } + + public boolean isFinal() { + return this.isFinal; + } + + @Override + public State with(Transition tr) { + this.transitions.add(tr); + return this; + } + +} diff --git a/algorithms/src/main/java/com/baeldung/automata/RtTransition.java b/algorithms/src/main/java/com/baeldung/automata/RtTransition.java new file mode 100644 index 000000000000..560011e42a1e --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/automata/RtTransition.java @@ -0,0 +1,31 @@ +package com.baeldung.automata; + + +/** + * Transition in finite state machine. + */ +public final class RtTransition implements Transition { + + private String rule; + private State next; + + /** + * Ctor. + * @param rule Rule that a character has to meet + * in order to get to the next state. + * @param next Next state. + */ + public RtTransition (String rule, State next) { + this.rule = rule; + this.next = next; + } + + public State state() { + return this.next; + } + + public boolean isPossible(CharSequence c) { + return this.rule.equalsIgnoreCase(String.valueOf(c)); + } + +} diff --git a/algorithms/src/main/java/com/baeldung/automata/State.java b/algorithms/src/main/java/com/baeldung/automata/State.java new file mode 100644 index 000000000000..a25af9d03a50 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/automata/State.java @@ -0,0 +1,29 @@ +package com.baeldung.automata; + +/** + * State. Part of a finite state machine. + */ +public interface State { + + /** + * Add a Transition to this state. + * @param tr Given transition. + * @return Modified State. + */ + State with(final Transition tr); + + /** + * Follow one of the transitions, to get + * to the next state. + * @param c Character. + * @return State. + * @throws IllegalStateException if the char is not accepted. + */ + State transit(final CharSequence c); + + /** + * Can the automaton stop on this state? + * @return true or false + */ + boolean isFinal(); +} diff --git a/algorithms/src/main/java/com/baeldung/automata/Transition.java b/algorithms/src/main/java/com/baeldung/automata/Transition.java new file mode 100644 index 000000000000..d57620f911c2 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/automata/Transition.java @@ -0,0 +1,20 @@ +package com.baeldung.automata; + +/** + * Transition in a finite State machine. + */ +public interface Transition { + + /** + * Is the transition possible with the given character? + * @param c char. + * @return true or false. + */ + boolean isPossible(final CharSequence c); + + /** + * The state to which this transition leads. + * @return State. + */ + State state(); +} diff --git a/core-java/src/test/java/com/baeldung/algorithms/AntColonyOptimizationTest.java b/algorithms/src/test/java/algorithms/AntColonyOptimizationTest.java similarity index 83% rename from core-java/src/test/java/com/baeldung/algorithms/AntColonyOptimizationTest.java rename to algorithms/src/test/java/algorithms/AntColonyOptimizationTest.java index cd8efaa1067e..a11f5db69851 100644 --- a/core-java/src/test/java/com/baeldung/algorithms/AntColonyOptimizationTest.java +++ b/algorithms/src/test/java/algorithms/AntColonyOptimizationTest.java @@ -1,4 +1,4 @@ -package com.baeldung.algorithms; +package algorithms; import org.junit.Assert; import org.junit.Test; @@ -16,7 +16,7 @@ public void testGenerateRandomMatrix() { @Test public void testStartAntOptimization() { AntColonyOptimization antTSP = new AntColonyOptimization(5); - Assert.assertNotNull(antTSP.startAntOptimization()); + Assert.assertNotNull(antTSP.solve()); } } diff --git a/core-java/src/test/java/com/baeldung/algorithms/BinaryGeneticAlgorithmUnitTest.java b/algorithms/src/test/java/algorithms/BinaryGeneticAlgorithmUnitTest.java similarity index 91% rename from core-java/src/test/java/com/baeldung/algorithms/BinaryGeneticAlgorithmUnitTest.java rename to algorithms/src/test/java/algorithms/BinaryGeneticAlgorithmUnitTest.java index 2e92177c8c50..2ba516d31039 100644 --- a/core-java/src/test/java/com/baeldung/algorithms/BinaryGeneticAlgorithmUnitTest.java +++ b/algorithms/src/test/java/algorithms/BinaryGeneticAlgorithmUnitTest.java @@ -1,4 +1,4 @@ -package com.baeldung.algorithms; +package algorithms; import org.junit.Assert; import org.junit.Test; diff --git a/algorithms/src/test/java/algorithms/DijkstraAlgorithmTest.java b/algorithms/src/test/java/algorithms/DijkstraAlgorithmTest.java index 07606bde4bb5..4d8cebed25e4 100644 --- a/algorithms/src/test/java/algorithms/DijkstraAlgorithmTest.java +++ b/algorithms/src/test/java/algorithms/DijkstraAlgorithmTest.java @@ -1,10 +1,11 @@ package algorithms; -import com.baeldung.algorithms.dijkstra.Dijkstra; -import com.baeldung.algorithms.dijkstra.Graph; -import com.baeldung.algorithms.dijkstra.Node; import org.junit.Test; +import com.baeldung.algorithms.ga.dijkstra.Dijkstra; +import com.baeldung.algorithms.ga.dijkstra.Graph; +import com.baeldung.algorithms.ga.dijkstra.Node; + import java.util.Arrays; import java.util.List; diff --git a/algorithms/src/test/java/algorithms/RtFiniteStateMachineTest.java b/algorithms/src/test/java/algorithms/RtFiniteStateMachineTest.java new file mode 100644 index 000000000000..089b38ec3fa6 --- /dev/null +++ b/algorithms/src/test/java/algorithms/RtFiniteStateMachineTest.java @@ -0,0 +1,82 @@ +package algorithms; + +import static org.junit.Assert.*; +import org.junit.Test; +import com.baeldung.automata.*; + +/** + * Tests for {@link RtFiniteStateMachine} + */ +public final class RtFiniteStateMachineTest { + + @Test + public void acceptsSimplePair() { + String json = "{\"key\":\"value\"}"; + FiniteStateMachine machine = this.buildJsonStateMachine(); + for (int i=0;iorg.apache.httpcomponents httpclient ${httpclient.version} + + + commons-logging + commons-logging + + diff --git a/apache-cxf/cxf-spring/pom.xml b/apache-cxf/cxf-spring/pom.xml index 79a7650cedaa..dffcc3ee1bcb 100644 --- a/apache-cxf/cxf-spring/pom.xml +++ b/apache-cxf/cxf-spring/pom.xml @@ -24,6 +24,12 @@ org.springframework spring-context ${spring.version} + + + commons-logging + commons-logging + + org.springframework diff --git a/apache-fop/pom.xml b/apache-fop/pom.xml index 6f89497a7d01..2de9611ad104 100644 --- a/apache-fop/pom.xml +++ b/apache-fop/pom.xml @@ -28,11 +28,6 @@ ${org.slf4j.version} - - org.slf4j - log4j-over-slf4j - ${org.slf4j.version} - @@ -78,6 +73,10 @@ org.apache.avalon.framework avalon-framework-impl + + commons-logging + commons-logging + @@ -90,6 +89,12 @@ avalon-framework avalon-framework-impl ${avalon-framework.version} + + + commons-logging + commons-logging + + @@ -101,9 +106,7 @@ org.dbdoclet herold - 6.1.0 - system - ${basedir}/src/test/resources/jars/herold.jar + 8.0.4 @@ -140,6 +143,8 @@ maven-surefire-plugin ${maven-surefire-plugin.version} + 3 + true **/*IntegrationTest.java **/*LiveTest.java diff --git a/apache-fop/src/main/resources/logback.xml b/apache-fop/src/main/resources/logback.xml index 62d0ea5037b2..ec0dc2469ae0 100644 --- a/apache-fop/src/main/resources/logback.xml +++ b/apache-fop/src/main/resources/logback.xml @@ -1,5 +1,5 @@ + - web - %date [%thread] %-5level %logger{36} - %message%n @@ -7,10 +7,13 @@ - + + + + + - \ No newline at end of file diff --git a/apache-fop/src/test/java/org/baeldung/java/ApacheFOPConvertHTMLIntegrationTest.java b/apache-fop/src/test/java/org/baeldung/java/ApacheFOPConvertHTMLIntegrationTest.java index 99487c8fdf83..5e2da6fd1e41 100644 --- a/apache-fop/src/test/java/org/baeldung/java/ApacheFOPConvertHTMLIntegrationTest.java +++ b/apache-fop/src/test/java/org/baeldung/java/ApacheFOPConvertHTMLIntegrationTest.java @@ -19,21 +19,21 @@ import org.apache.fop.apps.Fop; import org.apache.fop.apps.FopFactory; import org.apache.xmlgraphics.util.MimeConstants; -import org.dbdoclet.trafo.html.docbook.DocBookTransformer; +import org.dbdoclet.trafo.html.docbook.HtmlDocBookTrafo; import org.dbdoclet.trafo.script.Script; import org.junit.Test; import org.w3c.dom.Document; import org.w3c.tidy.Tidy; public class ApacheFOPConvertHTMLIntegrationTest { - private String inputFile = "src/test/resources/input.html"; - private String style = "src/test/resources/xhtml2fo.xsl"; - private String style1 = "src/test/resources/docbook-xsl/fo/docbook.xsl"; - private String output_jtidy = "src/test/resources/output_jtidy.pdf"; - private String output_html2fo = "src/test/resources/output_html2fo.pdf"; - private String output_herold = "src/test/resources/output_herold.pdf"; - private String foFile = "src/test/resources/input.fo"; - private String xmlFile = "src/test/resources/input.xml"; + private final String inputFile = "src/test/resources/input.html"; + private final String style = "src/test/resources/xhtml2fo.xsl"; + private final String style1 = "src/test/resources/docbook-xsl/fo/docbook.xsl"; + private final String output_jtidy = "src/test/resources/output_jtidy.pdf"; + private final String output_html2fo = "src/test/resources/output_html2fo.pdf"; + private final String output_herold = "src/test/resources/output_herold.pdf"; + private final String foFile = "src/test/resources/input.fo"; + private final String xmlFile = "src/test/resources/input.xml"; @Test public void whenTransformHTMLToPDFUsingJTidy_thenCorrect() throws Exception { @@ -114,8 +114,9 @@ private Document fromXMLFileToFO() throws Exception { private void fromHTMLTOXMLUsingHerold() throws Exception { final Script script = new Script(); - final DocBookTransformer transformer = new DocBookTransformer(); - transformer.setScript(script); - transformer.convert(new FileInputStream(inputFile), new FileOutputStream(xmlFile)); + final HtmlDocBookTrafo transformer = new HtmlDocBookTrafo(); + transformer.setInputStream(new FileInputStream(inputFile)); + transformer.setOutputStream(new FileOutputStream(xmlFile)); + transformer.transform(script); } } diff --git a/apache-fop/src/test/java/org/baeldung/java/ApacheFOPHeroldLiveTest.java b/apache-fop/src/test/java/org/baeldung/java/ApacheFOPHeroldLiveTest.java index 9e71cd9c1609..849622239408 100644 --- a/apache-fop/src/test/java/org/baeldung/java/ApacheFOPHeroldLiveTest.java +++ b/apache-fop/src/test/java/org/baeldung/java/ApacheFOPHeroldLiveTest.java @@ -10,6 +10,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.net.HttpURLConnection; import java.net.URL; import javax.xml.transform.Result; @@ -25,19 +26,15 @@ import org.apache.fop.apps.FopFactory; import org.apache.xmlgraphics.util.MimeConstants; import org.dbdoclet.trafo.TrafoScriptManager; -import org.dbdoclet.trafo.html.docbook.DocBookTransformer; +import org.dbdoclet.trafo.html.docbook.HtmlDocBookTrafo; import org.dbdoclet.trafo.script.Script; import org.junit.Test; import org.w3c.dom.Document; public class ApacheFOPHeroldLiveTest { - private String[] inputUrls = {// @formatter:off - "http://www.baeldung.com/2011/10/20/bootstraping-a-web-application-with-spring-3-1-and-java-based-configuration-part-1/", - "http://www.baeldung.com/2011/10/25/building-a-restful-web-service-with-spring-3-1-and-java-based-configuration-part-2/", - "http://www.baeldung.com/2011/10/31/securing-a-restful-web-service-with-spring-security-3-1-part-3/", - "http://www.baeldung.com/spring-security-basic-authentication", - "http://www.baeldung.com/spring-security-digest-authentication", - "http://www.baeldung.com/2011/11/20/basic-and-digest-authentication-for-a-restful-service-with-spring-security-3-1/", + private final String[] inputUrls = {// @formatter:off + // "http://www.baeldung.com/spring-security-basic-authentication", + "http://www.baeldung.com/spring-security-digest-authentication" //"http://www.baeldung.com/spring-httpmessageconverter-rest", //"http://www.baeldung.com/2011/11/06/restful-web-service-discoverability-part-4/", //"http://www.baeldung.com/2011/11/13/rest-service-discoverability-with-spring-part-5/", @@ -49,10 +46,10 @@ public class ApacheFOPHeroldLiveTest { //"http://www.baeldung.com/2013/01/18/testing-rest-with-multiple-mime-types/" }; // @formatter:on - private String style_file = "src/test/resources/docbook-xsl/fo/docbook.xsl"; - private String output_file = "src/test/resources/final_output.pdf"; - private String xmlInput = "src/test/resources/input.xml"; - private String xmlOutput = "src/test/resources/output.xml"; + private final String style_file = "src/test/resources/docbook-xsl/fo/docbook.xsl"; + private final String output_file = "src/test/resources/final_output.pdf"; + private final String xmlInput = "src/test/resources/input.xml"; + private final String xmlOutput = "src/test/resources/output.xml"; // tests @@ -75,10 +72,11 @@ private void fromHTMLTOXMLUsingHerold(final String input, final boolean append) final TrafoScriptManager mgr = new TrafoScriptManager(); final File profileFile = new File("src/test/resources/default.her"); script = mgr.parseScript(profileFile); - final DocBookTransformer transformer = new DocBookTransformer(); - transformer.setScript(script); + final HtmlDocBookTrafo transformer = new HtmlDocBookTrafo(); + transformer.setInputStream(getInputStream(input)); + transformer.setOutputStream(new FileOutputStream(xmlInput, append)); - transformer.convert(getInputStream(input), new FileOutputStream(xmlInput, append)); + transformer.transform(script); } private Document fromXMLFileToFO() throws Exception { @@ -112,7 +110,9 @@ private Transformer createTransformer(final String styleFile) throws Exception { private InputStream getInputStream(final String input) throws IOException { final URL url = new URL(input); - return url.openStream(); + final HttpURLConnection httpcon = (HttpURLConnection) url.openConnection(); + httpcon.addRequestProperty("User-Agent", "Mozilla/4.0"); + return httpcon.getInputStream(); } private void fixXML(final String input, final String output) throws IOException { @@ -127,7 +127,7 @@ private void fixXML(final String input, final String output) throws IOException if (line.contains("info>")) { writer.write(line.replace("info>", "section>")); - } else if (!((line.startsWith(" 4)) { + } else if (!((line.startsWith(" 4))) { writer.write(line.replaceAll("xml:id=\"", "xml:id=\"" + count)); } writer.write("\n"); diff --git a/apache-fop/src/test/resources/jars/herold.jar b/apache-fop/src/test/resources/jars/herold.jar deleted file mode 100644 index ef5d052f3667..000000000000 Binary files a/apache-fop/src/test/resources/jars/herold.jar and /dev/null differ diff --git a/apache-poi/.gitignore b/apache-poi/.gitignore index e05054868cbd..9552c1e63d01 100644 --- a/apache-poi/.gitignore +++ b/apache-poi/.gitignore @@ -1 +1,3 @@ *.docx +temp.xls +temp.xlsx diff --git a/apache-poi/pom.xml b/apache-poi/pom.xml index d8a2cc72e078..b619d3adcb51 100644 --- a/apache-poi/pom.xml +++ b/apache-poi/pom.xml @@ -42,6 +42,12 @@ org.jxls jxls-jexcel ${jexcel.version} + + + commons-logging + commons-logging + + diff --git a/apache-poi/temp.xlsx b/apache-poi/temp.xlsx index 5281b2c4de3e..431a8a662c2c 100644 Binary files a/apache-poi/temp.xlsx and b/apache-poi/temp.xlsx differ diff --git a/apache-solrj/README.md b/apache-solrj/README.md new file mode 100644 index 000000000000..7a32becb644e --- /dev/null +++ b/apache-solrj/README.md @@ -0,0 +1,4 @@ +## Apache Solrj Tutorials Project + +### Relevant Articles +- [Guide to Solr in Java with Apache Solrj](http://www.baeldung.com/apache-solrj) diff --git a/apache-solrj/src/main/java/com/baeldung/solrjava/ProductBean.java b/apache-solrj/src/main/java/com/baeldung/solrjava/ProductBean.java new file mode 100644 index 000000000000..14eea8f2f9e2 --- /dev/null +++ b/apache-solrj/src/main/java/com/baeldung/solrjava/ProductBean.java @@ -0,0 +1,44 @@ +package com.baeldung.solrjava; + +import org.apache.solr.client.solrj.beans.Field; + +public class ProductBean { + + String id; + String name; + String price; + + public ProductBean(String id, String name, String price) { + super(); + this.id = id; + this.name = name; + this.price = price; + } + + public String getId() { + return id; + } + + @Field("id") + protected void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + @Field("name") + protected void setName(String name) { + this.name = name; + } + + public String getPrice() { + return price; + } + + @Field("price") + protected void setPrice(String price) { + this.price = price; + } +} diff --git a/apache-solrj/src/main/java/com/baeldung/solrjava/SolrJavaIntegration.java b/apache-solrj/src/main/java/com/baeldung/solrjava/SolrJavaIntegration.java index f2d21f0993ad..c55e1c9ada10 100644 --- a/apache-solrj/src/main/java/com/baeldung/solrjava/SolrJavaIntegration.java +++ b/apache-solrj/src/main/java/com/baeldung/solrjava/SolrJavaIntegration.java @@ -17,6 +17,12 @@ public SolrJavaIntegration(String clientUrl) { solrClient.setParser(new XMLResponseParser()); } + public void addProductBean(ProductBean pBean) throws IOException, SolrServerException { + + solrClient.addBean(pBean); + solrClient.commit(); + } + public void addSolrDocument(String documentId, String itemName, String itemPrice) throws SolrServerException, IOException { SolrInputDocument document = new SolrInputDocument(); @@ -27,12 +33,18 @@ public void addSolrDocument(String documentId, String itemName, String itemPrice solrClient.commit(); } - public void deleteSolrDocument(String documentId) throws SolrServerException, IOException { + public void deleteSolrDocumentById(String documentId) throws SolrServerException, IOException { solrClient.deleteById(documentId); solrClient.commit(); } + public void deleteSolrDocumentByQuery(String query) throws SolrServerException, IOException { + + solrClient.deleteByQuery(query); + solrClient.commit(); + } + protected HttpSolrClient getSolrClient() { return solrClient; } @@ -40,4 +52,5 @@ protected HttpSolrClient getSolrClient() { protected void setSolrClient(HttpSolrClient solrClient) { this.solrClient = solrClient; } + } diff --git a/apache-solrj/src/test/java/com/baeldung/solrjava/SolrJavaIntegrationTest.java b/apache-solrj/src/test/java/com/baeldung/solrjava/SolrJavaIntegrationTest.java index 22f9eae8ee8e..8b5fe77c6f67 100644 --- a/apache-solrj/src/test/java/com/baeldung/solrjava/SolrJavaIntegrationTest.java +++ b/apache-solrj/src/test/java/com/baeldung/solrjava/SolrJavaIntegrationTest.java @@ -24,7 +24,7 @@ public void setUp() throws Exception { } @Test - public void whenAdd_thenVerifyAdded() throws SolrServerException, IOException { + public void whenAdd_thenVerifyAddedByQueryOnId() throws SolrServerException, IOException { SolrQuery query = new SolrQuery(); query.set("q", "id:123456"); @@ -33,18 +33,68 @@ public void whenAdd_thenVerifyAdded() throws SolrServerException, IOException { response = solrJavaIntegration.getSolrClient().query(query); SolrDocumentList docList = response.getResults(); - assertEquals(docList.getNumFound(), 1); + assertEquals(1, docList.getNumFound()); for (SolrDocument doc : docList) { - assertEquals((String) doc.getFieldValue("id"), "123456"); - assertEquals((Double) doc.getFieldValue("price"), (Double) 599.99); + assertEquals("Kenmore Dishwasher", (String) doc.getFieldValue("name")); + assertEquals((Double) 599.99, (Double) doc.getFieldValue("price")); } } @Test - public void whenDelete_thenVerifyDeleted() throws SolrServerException, IOException { + public void whenAdd_thenVerifyAddedByQueryOnPrice() throws SolrServerException, IOException { - solrJavaIntegration.deleteSolrDocument("123456"); + SolrQuery query = new SolrQuery(); + query.set("q", "price:599.99"); + QueryResponse response = null; + + response = solrJavaIntegration.getSolrClient().query(query); + + SolrDocumentList docList = response.getResults(); + assertEquals(1, docList.getNumFound()); + + for (SolrDocument doc : docList) { + assertEquals("123456", (String) doc.getFieldValue("id")); + assertEquals((Double) 599.99, (Double) doc.getFieldValue("price")); + } + } + + @Test + public void whenAdd_thenVerifyAddedByQuery() throws SolrServerException, IOException { + + SolrDocument doc = solrJavaIntegration.getSolrClient().getById("123456"); + assertEquals("Kenmore Dishwasher", (String) doc.getFieldValue("name")); + assertEquals((Double) 599.99, (Double) doc.getFieldValue("price")); + } + + @Test + public void whenAddBean_thenVerifyAddedByQuery() throws SolrServerException, IOException { + + ProductBean pBean = new ProductBean("888", "Apple iPhone 6s", "299.99"); + solrJavaIntegration.addProductBean(pBean); + + SolrDocument doc = solrJavaIntegration.getSolrClient().getById("888"); + assertEquals("Apple iPhone 6s", (String) doc.getFieldValue("name")); + assertEquals((Double) 299.99, (Double) doc.getFieldValue("price")); + } + + @Test + public void whenDeleteById_thenVerifyDeleted() throws SolrServerException, IOException { + + solrJavaIntegration.deleteSolrDocumentById("123456"); + + SolrQuery query = new SolrQuery(); + query.set("q", "id:123456"); + QueryResponse response = solrJavaIntegration.getSolrClient().query(query); + + SolrDocumentList docList = response.getResults(); + assertEquals(0, docList.getNumFound()); + } + + @Test + public void whenDeleteByQuery_thenVerifyDeleted() throws SolrServerException, IOException { + + solrJavaIntegration.deleteSolrDocumentByQuery("name:Kenmore Dishwasher"); SolrQuery query = new SolrQuery(); query.set("q", "id:123456"); @@ -53,6 +103,6 @@ public void whenDelete_thenVerifyDeleted() throws SolrServerException, IOExcepti response = solrJavaIntegration.getSolrClient().query(query); SolrDocumentList docList = response.getResults(); - assertEquals(docList.getNumFound(), 0); + assertEquals(0, docList.getNumFound()); } } diff --git a/apache-thrift/pom.xml b/apache-thrift/pom.xml index 66cfb2bb41da..e275dc0636f6 100644 --- a/apache-thrift/pom.xml +++ b/apache-thrift/pom.xml @@ -19,6 +19,12 @@ org.apache.thrift libthrift ${thrift.version} + + + commons-logging + commons-logging + + diff --git a/apache-velocity/pom.xml b/apache-velocity/pom.xml index 08f0e96a58de..06cfeabfa896 100644 --- a/apache-velocity/pom.xml +++ b/apache-velocity/pom.xml @@ -38,7 +38,7 @@ org.apache.velocity velocity-tools - ${velocity-tools-version} + ${velocity-tools-version} org.slf4j @@ -55,6 +55,12 @@ httpclient ${org.apache.httpcomponents.version} test + + + commons-logging + commons-logging + + diff --git a/apache-velocity/src/main/resources/logback.xml b/apache-velocity/src/main/resources/logback.xml index 70a420a57ad2..ec0dc2469ae0 100644 --- a/apache-velocity/src/main/resources/logback.xml +++ b/apache-velocity/src/main/resources/logback.xml @@ -1,23 +1,19 @@ - - - + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n - + + - - - - - - - - - - + + + + + \ No newline at end of file diff --git a/aspectj/pom.xml b/aspectj/pom.xml index 90b527c14f6b..2ef2ae9df791 100644 --- a/aspectj/pom.xml +++ b/aspectj/pom.xml @@ -58,7 +58,7 @@ org.springframework spring-core - 4.3.4.RELEASE + 4.3.4.RELEASE cglib @@ -70,11 +70,6 @@ spring-aop 4.3.4.RELEASE - - log4j - log4j - 1.2.17 - diff --git a/aspectj/src/main/resources/log4j.properties b/aspectj/src/main/resources/log4j.properties deleted file mode 100644 index 9e2afcd5b0e6..000000000000 --- a/aspectj/src/main/resources/log4j.properties +++ /dev/null @@ -1,10 +0,0 @@ -log4j.rootLogger=TRACE, stdout - -# Redirect log messages to console -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.Target=System.out -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n - -log4j.logger.org.springframework.aop.interceptor.PerformanceMonitorInterceptor=TRACE, stdout -log4j.logger.com.baeldung.performancemonitor.MyPerformanceMonitorInterceptor=INFO, stdout \ No newline at end of file diff --git a/aspectj/src/main/resources/logback.xml b/aspectj/src/main/resources/logback.xml index 8b566286b88d..ec0dc2469ae0 100644 --- a/aspectj/src/main/resources/logback.xml +++ b/aspectj/src/main/resources/logback.xml @@ -1,18 +1,19 @@ + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - - %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg %n - - + + - - - + + - - - - + + + \ No newline at end of file diff --git a/aws/README.md b/aws/README.md new file mode 100644 index 000000000000..10db004765d2 --- /dev/null +++ b/aws/README.md @@ -0,0 +1,3 @@ +### Relevant articles + +- [AWS Lambda Using DynamoDB With Java](http://www.baeldung.com/aws-lambda-dynamodb-java) diff --git a/aws/pom.xml b/aws/pom.xml index 681b76cfd41b..b493dc58e8e9 100644 --- a/aws/pom.xml +++ b/aws/pom.xml @@ -19,12 +19,24 @@ com.amazonaws aws-lambda-java-core ${aws-lambda-java-core.version} + + + commons-logging + commons-logging + + com.amazonaws aws-lambda-java-events ${aws-lambda-java-events.version} + + + commons-logging + commons-logging + + diff --git a/axon/README.md b/axon/README.md new file mode 100644 index 000000000000..f1ae5d00d872 --- /dev/null +++ b/axon/README.md @@ -0,0 +1,3 @@ +### Relevant articles + +- [A Guide to the Axon Framework](http://www.baeldung.com/axon-cqrs-event-sourcing) diff --git a/axon/pom.xml b/axon/pom.xml new file mode 100644 index 000000000000..2bffa53bb82c --- /dev/null +++ b/axon/pom.xml @@ -0,0 +1,52 @@ + + + + parent-modules + com.baeldung + 1.0.0-SNAPSHOT + + 4.0.0 + + axon + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + + + + + org.axonframework + axon-test + ${axon.version} + test + + + org.axonframework + axon-core + ${axon.version} + + + junit + junit + ${junit.version} + test + + + + + 3.0.2 + 4.12 + + + + \ No newline at end of file diff --git a/axon/src/main/java/com/baeldung/axon/MessagesRunner.java b/axon/src/main/java/com/baeldung/axon/MessagesRunner.java new file mode 100644 index 000000000000..77b50d09bd49 --- /dev/null +++ b/axon/src/main/java/com/baeldung/axon/MessagesRunner.java @@ -0,0 +1,54 @@ +package com.baeldung.axon; + +import com.baeldung.axon.aggregates.MessagesAggregate; +import com.baeldung.axon.commands.CreateMessageCommand; +import com.baeldung.axon.commands.MarkReadMessageCommand; +import com.baeldung.axon.eventhandlers.MessagesEventHandler; +import org.axonframework.commandhandling.AggregateAnnotationCommandHandler; +import org.axonframework.commandhandling.CommandBus; +import org.axonframework.commandhandling.SimpleCommandBus; +import org.axonframework.commandhandling.gateway.CommandGateway; +import org.axonframework.commandhandling.gateway.DefaultCommandGateway; +import org.axonframework.eventhandling.AnnotationEventListenerAdapter; +import org.axonframework.eventsourcing.EventSourcingRepository; +import org.axonframework.eventsourcing.eventstore.EmbeddedEventStore; +import org.axonframework.eventsourcing.eventstore.EventStore; +import org.axonframework.eventsourcing.eventstore.inmemory.InMemoryEventStorageEngine; + +import java.util.UUID; + +public class MessagesRunner { + + public static void main(String[] args) { + CommandBus commandBus = new SimpleCommandBus(); + + CommandGateway commandGateway = new DefaultCommandGateway(commandBus); + + EventStore eventStore = new EmbeddedEventStore(new InMemoryEventStorageEngine()); + + EventSourcingRepository repository = + new EventSourcingRepository<>(MessagesAggregate.class, eventStore); + + + AggregateAnnotationCommandHandler messagesAggregateAggregateAnnotationCommandHandler = + new AggregateAnnotationCommandHandler(MessagesAggregate.class, repository); + messagesAggregateAggregateAnnotationCommandHandler.subscribe(commandBus); + + final AnnotationEventListenerAdapter annotationEventListenerAdapter = + new AnnotationEventListenerAdapter(new MessagesEventHandler()); + eventStore.subscribe(eventMessages -> eventMessages.forEach(e -> { + try { + annotationEventListenerAdapter.handle(e); + } catch (Exception e1) { + throw new RuntimeException(e1); + + } + } + + )); + + final String itemId = UUID.randomUUID().toString(); + commandGateway.send(new CreateMessageCommand(itemId, "Hello, how is your day? :-)")); + commandGateway.send(new MarkReadMessageCommand(itemId)); + } +} \ No newline at end of file diff --git a/axon/src/main/java/com/baeldung/axon/aggregates/MessagesAggregate.java b/axon/src/main/java/com/baeldung/axon/aggregates/MessagesAggregate.java new file mode 100644 index 000000000000..e762604b74e8 --- /dev/null +++ b/axon/src/main/java/com/baeldung/axon/aggregates/MessagesAggregate.java @@ -0,0 +1,36 @@ +package com.baeldung.axon.aggregates; + +import com.baeldung.axon.commands.CreateMessageCommand; +import com.baeldung.axon.commands.MarkReadMessageCommand; +import com.baeldung.axon.events.MessageCreatedEvent; +import com.baeldung.axon.events.MessageReadEvent; +import org.axonframework.commandhandling.CommandHandler; +import org.axonframework.commandhandling.model.AggregateIdentifier; +import org.axonframework.eventhandling.EventHandler; + +import static org.axonframework.commandhandling.model.AggregateLifecycle.apply; + + +public class MessagesAggregate { + + @AggregateIdentifier + private String id; + + public MessagesAggregate() { + } + + @CommandHandler + public MessagesAggregate(CreateMessageCommand command) { + apply(new MessageCreatedEvent(command.getId(), command.getText())); + } + + @EventHandler + public void on(MessageCreatedEvent event) { + this.id = event.getId(); + } + + @CommandHandler + public void markRead(MarkReadMessageCommand command) { + apply(new MessageReadEvent(id)); + } +} \ No newline at end of file diff --git a/axon/src/main/java/com/baeldung/axon/commands/CreateMessageCommand.java b/axon/src/main/java/com/baeldung/axon/commands/CreateMessageCommand.java new file mode 100644 index 000000000000..d0651bf12ecf --- /dev/null +++ b/axon/src/main/java/com/baeldung/axon/commands/CreateMessageCommand.java @@ -0,0 +1,24 @@ +package com.baeldung.axon.commands; + + +import org.axonframework.commandhandling.TargetAggregateIdentifier; + +public class CreateMessageCommand { + + @TargetAggregateIdentifier + private final String id; + private final String text; + + public CreateMessageCommand(String id, String text) { + this.id = id; + this.text = text; + } + + public String getId() { + return id; + } + + public String getText() { + return text; + } +} \ No newline at end of file diff --git a/axon/src/main/java/com/baeldung/axon/commands/MarkReadMessageCommand.java b/axon/src/main/java/com/baeldung/axon/commands/MarkReadMessageCommand.java new file mode 100644 index 000000000000..e66582d9ec78 --- /dev/null +++ b/axon/src/main/java/com/baeldung/axon/commands/MarkReadMessageCommand.java @@ -0,0 +1,18 @@ +package com.baeldung.axon.commands; + + +import org.axonframework.commandhandling.TargetAggregateIdentifier; + +public class MarkReadMessageCommand { + + @TargetAggregateIdentifier + private final String id; + + public MarkReadMessageCommand(String id) { + this.id = id; + } + + public String getId() { + return id; + } +} \ No newline at end of file diff --git a/axon/src/main/java/com/baeldung/axon/eventhandlers/MessagesEventHandler.java b/axon/src/main/java/com/baeldung/axon/eventhandlers/MessagesEventHandler.java new file mode 100644 index 000000000000..3e51e19c4e9b --- /dev/null +++ b/axon/src/main/java/com/baeldung/axon/eventhandlers/MessagesEventHandler.java @@ -0,0 +1,19 @@ +package com.baeldung.axon.eventhandlers; + +import com.baeldung.axon.events.MessageReadEvent; +import com.baeldung.axon.events.MessageCreatedEvent; +import org.axonframework.eventhandling.EventHandler; + + +public class MessagesEventHandler { + + @EventHandler + public void handle(MessageCreatedEvent event) { + System.out.println("Message received: " + event.getText() + " (" + event.getId() + ")"); + } + + @EventHandler + public void handle(MessageReadEvent event) { + System.out.println("Message read: " + event.getId()); + } +} \ No newline at end of file diff --git a/axon/src/main/java/com/baeldung/axon/events/MessageCreatedEvent.java b/axon/src/main/java/com/baeldung/axon/events/MessageCreatedEvent.java new file mode 100644 index 000000000000..3c9aac5ed8fd --- /dev/null +++ b/axon/src/main/java/com/baeldung/axon/events/MessageCreatedEvent.java @@ -0,0 +1,20 @@ +package com.baeldung.axon.events; + +public class MessageCreatedEvent { + + private final String id; + private final String text; + + public MessageCreatedEvent(String id, String text) { + this.id = id; + this.text = text; + } + + public String getId() { + return id; + } + + public String getText() { + return text; + } +} \ No newline at end of file diff --git a/axon/src/main/java/com/baeldung/axon/events/MessageReadEvent.java b/axon/src/main/java/com/baeldung/axon/events/MessageReadEvent.java new file mode 100644 index 000000000000..57bfc8e19e3e --- /dev/null +++ b/axon/src/main/java/com/baeldung/axon/events/MessageReadEvent.java @@ -0,0 +1,14 @@ +package com.baeldung.axon.events; + +public class MessageReadEvent { + + private final String id; + + public MessageReadEvent(String id) { + this.id = id; + } + + public String getId() { + return id; + } +} \ No newline at end of file diff --git a/axon/src/test/java/com/baeldung/axon/MessagesAggregateTest.java b/axon/src/test/java/com/baeldung/axon/MessagesAggregateTest.java new file mode 100644 index 000000000000..bbeff18f2745 --- /dev/null +++ b/axon/src/test/java/com/baeldung/axon/MessagesAggregateTest.java @@ -0,0 +1,42 @@ +package com.baeldung.axon; + +import com.baeldung.axon.aggregates.MessagesAggregate; +import com.baeldung.axon.commands.CreateMessageCommand; +import com.baeldung.axon.commands.MarkReadMessageCommand; +import com.baeldung.axon.events.MessageCreatedEvent; +import com.baeldung.axon.events.MessageReadEvent; +import org.axonframework.test.aggregate.AggregateTestFixture; +import org.axonframework.test.aggregate.FixtureConfiguration; +import org.junit.Before; +import org.junit.Test; + +import java.util.UUID; + +public class MessagesAggregateTest { + + private FixtureConfiguration fixture; + + @Before + public void setUp() throws Exception { + fixture = new AggregateTestFixture(MessagesAggregate.class); + + } + + @Test + public void giveAggregateRoot_whenCreateMessageCommand_thenShouldProduceMessageCreatedEvent() throws Exception { + String eventText = "Hello, how is your day?"; + String id = UUID.randomUUID().toString(); + fixture.given() + .when(new CreateMessageCommand(id, eventText)) + .expectEvents(new MessageCreatedEvent(id, eventText)); + } + + @Test + public void givenMessageCreatedEvent_whenReadMessageCommand_thenShouldProduceMessageReadEvent() throws Exception { + String id = UUID.randomUUID().toString(); + + fixture.given(new MessageCreatedEvent(id, "Hello :-)")) + .when(new MarkReadMessageCommand(id)) + .expectEvents(new MessageReadEvent(id)); + } +} \ No newline at end of file diff --git a/core-java/0.004102810554955205 b/book similarity index 100% rename from core-java/0.004102810554955205 rename to book diff --git a/cdi/pom.xml b/cdi/pom.xml index e5aaeb2c7bde..d2fcec59d702 100644 --- a/cdi/pom.xml +++ b/cdi/pom.xml @@ -12,6 +12,12 @@ org.springframework spring-core ${spring.version} + + + commons-logging + commons-logging + + org.springframework diff --git a/core-java-9/README.md b/core-java-9/README.md index 53ad79e59c62..3e82ffe14b23 100644 --- a/core-java-9/README.md +++ b/core-java-9/README.md @@ -8,3 +8,8 @@ - [Java 9 Stream API Improvements](http://www.baeldung.com/java-9-stream-api) - [Java 9 Convenience Factory Methods for Collections](http://www.baeldung.com/java-9-collections-factory-methods) - [New Stream Collectors in Java 9](http://www.baeldung.com/java9-stream-collectors) +- [Java 9 CompletableFuture API Improvements](http://www.baeldung.com/java9-completablefuture-api-improvements/) +- [Spring Security – Redirect to the Previous URL After Login](http://www.baeldung.com/spring-security-redirect-login) +- [Java 9 Process API Improvements](http://www.baeldung.com/java-9-process-api) +- [Introduction to Java 9 StackWalking API](http://www.baeldung.com/java-9-stackwalking-api) +- [Introduction to Project Jigsaw](http://www.baeldung.com/project-jigsaw-java-modularity) diff --git a/core-java-9/compile-modules.sh b/core-java-9/compile-modules.sh new file mode 100644 index 000000000000..4c9521de75d5 --- /dev/null +++ b/core-java-9/compile-modules.sh @@ -0,0 +1 @@ +javac -d mods --module-source-path src/modules $(find src/modules -name "*.java") \ No newline at end of file diff --git a/core-java-9/compile-student-client.bat b/core-java-9/compile-student-client.bat new file mode 100644 index 000000000000..72b2774480dc --- /dev/null +++ b/core-java-9/compile-student-client.bat @@ -0,0 +1,3 @@ +javac --module-path mods -d mods/com.baeldung.student.client^ + src/modules/com.baeldung.student.client/module-info.java^ + src/modules/com.baeldung.student.client/com/baeldung/student/client/StudentClient.java \ No newline at end of file diff --git a/core-java-9/compile-student-model.bat b/core-java-9/compile-student-model.bat new file mode 100644 index 000000000000..902756c2740f --- /dev/null +++ b/core-java-9/compile-student-model.bat @@ -0,0 +1,2 @@ +javac -d mods/com.baeldung.student.model src/modules/com.baeldung.student.model/module-info.java^ + src/modules/com.baeldung.student.model/com/baeldung/student/model/Student.java \ No newline at end of file diff --git a/core-java-9/compile-student-service-dbimpl.bat b/core-java-9/compile-student-service-dbimpl.bat new file mode 100644 index 000000000000..bd1cfb7cfec1 --- /dev/null +++ b/core-java-9/compile-student-service-dbimpl.bat @@ -0,0 +1,3 @@ +javac --module-path mods -d mods/com.baeldung.student.service.dbimpl^ + src/modules/com.baeldung.student.service.dbimpl/module-info.java^ + src/modules/com.baeldung.student.service.dbimpl/com/baeldung/student/service/dbimpl/StudentDbService.java \ No newline at end of file diff --git a/core-java-9/compile-student-service.bat b/core-java-9/compile-student-service.bat new file mode 100644 index 000000000000..2892b237d1ab --- /dev/null +++ b/core-java-9/compile-student-service.bat @@ -0,0 +1,3 @@ +javac --module-path mods -d mods/com.baeldung.student.service^ + src/modules/com.baeldung.student.service/module-info.java^ + src/modules/com.baeldung.student.service/com/baeldung/student/service/StudentService.java \ No newline at end of file diff --git a/core-java-9/run-student-client.bat b/core-java-9/run-student-client.bat new file mode 100644 index 000000000000..2b78a26ec470 --- /dev/null +++ b/core-java-9/run-student-client.bat @@ -0,0 +1 @@ +java --module-path mods -m com.baeldung.student.client/com.baeldung.student.client.StudentClient \ No newline at end of file diff --git a/core-java-9/run-student-client.sh b/core-java-9/run-student-client.sh new file mode 100644 index 000000000000..2b78a26ec470 --- /dev/null +++ b/core-java-9/run-student-client.sh @@ -0,0 +1 @@ +java --module-path mods -m com.baeldung.student.client/com.baeldung.student.client.StudentClient \ No newline at end of file diff --git a/core-java-9/src/main/java/com/baeldung/java9/reactive/BaeldungBatchSubscriberImpl.java b/core-java-9/src/main/java/com/baeldung/java9/reactive/BaeldungBatchSubscriberImpl.java new file mode 100644 index 000000000000..46eee4883abe --- /dev/null +++ b/core-java-9/src/main/java/com/baeldung/java9/reactive/BaeldungBatchSubscriberImpl.java @@ -0,0 +1,82 @@ +package com.baeldung.java9.reactive; + +import java.util.ArrayList; +import java.util.concurrent.Flow.Subscriber; +import java.util.concurrent.Flow.Subscription; + +public class BaeldungBatchSubscriberImpl implements Subscriber { + private Subscription subscription; + private boolean completed = false; + private int counter; + private ArrayList buffer; + public static final int BUFFER_SIZE = 5; + + public BaeldungBatchSubscriberImpl() { + buffer = new ArrayList(); + } + + public boolean isCompleted() { + return completed; + } + + public void setCompleted(boolean completed) { + this.completed = completed; + } + + public int getCounter() { + return counter; + } + + public void setCounter(int counter) { + this.counter = counter; + } + + @Override + public void onSubscribe(Subscription subscription) { + this.subscription = subscription; + subscription.request(BUFFER_SIZE); + } + + @Override + public void onNext(String item) { + buffer.add(item); + // if buffer is full, process the items. + if (buffer.size() >= BUFFER_SIZE) { + processBuffer(); + } + //request more items. + subscription.request(1); + } + + private void processBuffer() { + if (buffer.isEmpty()) + return; + // Process all items in the buffer. Here, we just print it and sleep for 1 second. + System.out.print("Processed items: "); + buffer.stream() + .forEach(item -> { + System.out.print(" " + item); + }); + System.out.println(); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + counter = counter + buffer.size(); + buffer.clear(); + } + + @Override + public void onError(Throwable t) { + t.printStackTrace(); + } + + @Override + public void onComplete() { + completed = true; + // process any remaining items in buffer before + processBuffer(); + subscription.cancel(); + } +} diff --git a/core-java-9/src/main/java/com/baeldung/java9/reactive/BaeldungSubscriberImpl.java b/core-java-9/src/main/java/com/baeldung/java9/reactive/BaeldungSubscriberImpl.java new file mode 100644 index 000000000000..bacd777255ef --- /dev/null +++ b/core-java-9/src/main/java/com/baeldung/java9/reactive/BaeldungSubscriberImpl.java @@ -0,0 +1,55 @@ +package com.baeldung.java9.reactive; + +import java.util.concurrent.Flow.Subscriber; +import java.util.concurrent.Flow.Subscription; + +public class BaeldungSubscriberImpl implements Subscriber { + private Subscription subscription; + private boolean completed = false; + private int counter; + + public boolean isCompleted() { + return completed; + } + + public void setCompleted(boolean completed) { + this.completed = completed; + } + + public int getCounter() { + return counter; + } + + public void setCounter(int counter) { + this.counter = counter; + } + + @Override + public void onSubscribe(Subscription subscription) { + this.subscription = subscription; + subscription.request(1); + } + + @Override + public void onNext(String item) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + counter++; + System.out.println("Processed item : " + item); + subscription.request(1); + } + + @Override + public void onError(Throwable t) { + t.printStackTrace(); + } + + @Override + public void onComplete() { + completed = true; + subscription.cancel(); + } +} diff --git a/core-java-9/src/main/java/com/baeldung/java9/stackwalker/StackWalkerDemo.java b/core-java-9/src/main/java/com/baeldung/java9/stackwalker/StackWalkerDemo.java new file mode 100644 index 000000000000..a0e632d5694c --- /dev/null +++ b/core-java-9/src/main/java/com/baeldung/java9/stackwalker/StackWalkerDemo.java @@ -0,0 +1,84 @@ +package com.baeldung.java9.stackwalker; + +import java.lang.StackWalker.StackFrame; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class StackWalkerDemo { + + public void methodOne() { + this.methodTwo(); + } + + public void methodTwo() { + this.methodThree(); + } + + public void methodThree() { + List stackTrace = StackWalker.getInstance() + .walk(this::walkExample); + + printStackTrace(stackTrace); + + System.out.println("---------------------------------------------"); + + stackTrace = StackWalker.getInstance() + .walk(this::walkExample2); + + printStackTrace(stackTrace); + + System.out.println("---------------------------------------------"); + + String line = StackWalker.getInstance().walk(this::walkExample3); + System.out.println(line); + + System.out.println("---------------------------------------------"); + + stackTrace = StackWalker.getInstance(StackWalker.Option.SHOW_REFLECT_FRAMES) + .walk(this::walkExample); + + printStackTrace(stackTrace); + + System.out.println("---------------------------------------------"); + + Runnable r = () -> { + List stackTrace2 = StackWalker.getInstance(StackWalker.Option.SHOW_HIDDEN_FRAMES) + .walk(this::walkExample); + printStackTrace(stackTrace2); + }; + r.run(); + } + + public List walkExample(Stream stackFrameStream) { + return stackFrameStream.collect(Collectors.toList()); + } + + public List walkExample2(Stream stackFrameStream) { + return stackFrameStream.filter(frame -> frame.getClassName() + .contains("com.baeldung")) + .collect(Collectors.toList()); + } + + public String walkExample3(Stream stackFrameStream) { + return stackFrameStream.filter(frame -> frame.getClassName() + .contains("com.baeldung") + && frame.getClassName() + .endsWith("Test")) + .findFirst() + .map(frame -> frame.getClassName() + "#" + frame.getMethodName() + ", Line " + frame.getLineNumber()) + .orElse("Unknown caller"); + } + + public void findCaller() { + Class caller = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).getCallerClass(); + System.out.println(caller.getCanonicalName()); + } + + public void printStackTrace(List stackTrace) { + for (StackFrame stackFrame : stackTrace) { + System.out.println(stackFrame.getClassName() + .toString() + "#" + stackFrame.getMethodName() + ", Line " + stackFrame.getLineNumber()); + } + } +} diff --git a/core-java-9/src/main/resources/logback.xml b/core-java-9/src/main/resources/logback.xml index eefdc7a33769..ec0dc2469ae0 100644 --- a/core-java-9/src/main/resources/logback.xml +++ b/core-java-9/src/main/resources/logback.xml @@ -1,5 +1,5 @@ + - web - %date [%thread] %-5level %logger{36} - %message%n @@ -7,10 +7,13 @@ - + + + + + - + - \ No newline at end of file diff --git a/core-java-9/src/modules/com.baeldung.student.client/com/baeldung/student/client/StudentClient.java b/core-java-9/src/modules/com.baeldung.student.client/com/baeldung/student/client/StudentClient.java new file mode 100644 index 000000000000..e6fce9163f07 --- /dev/null +++ b/core-java-9/src/modules/com.baeldung.student.client/com/baeldung/student/client/StudentClient.java @@ -0,0 +1,16 @@ +package com.baeldung.student.client; + +import com.baeldung.student.service.StudentService; +import com.baeldung.student.service.dbimpl.StudentDbService; +import com.baeldung.student.model.Student; + +public class StudentClient { + + public static void main(String[] args) { + StudentService service = new StudentDbService(); + service.create(new Student()); + service.read("17SS0001"); + service.update(new Student()); + service.delete("17SS0001"); + } +} \ No newline at end of file diff --git a/core-java-9/src/modules/com.baeldung.student.client/module-info.java b/core-java-9/src/modules/com.baeldung.student.client/module-info.java new file mode 100644 index 000000000000..7ef7b430fc13 --- /dev/null +++ b/core-java-9/src/modules/com.baeldung.student.client/module-info.java @@ -0,0 +1,3 @@ +module com.baeldung.student.client{ + requires com.baeldung.student.service.dbimpl; +} \ No newline at end of file diff --git a/core-java-9/src/modules/com.baeldung.student.model/com/baeldung/student/model/Student.java b/core-java-9/src/modules/com.baeldung.student.model/com/baeldung/student/model/Student.java new file mode 100644 index 000000000000..d7f8f6910741 --- /dev/null +++ b/core-java-9/src/modules/com.baeldung.student.model/com/baeldung/student/model/Student.java @@ -0,0 +1,15 @@ +package com.baeldung.student.model; + +import java.util.Date; + +public class Student { + private String registrationId; + + public String getRegistrationId() { + return registrationId; + } + + public void setRegistrationId(String registrationId) { + this.registrationId = registrationId; + } +} \ No newline at end of file diff --git a/core-java-9/src/modules/com.baeldung.student.model/module-info.java b/core-java-9/src/modules/com.baeldung.student.model/module-info.java new file mode 100644 index 000000000000..3bdab058d43c --- /dev/null +++ b/core-java-9/src/modules/com.baeldung.student.model/module-info.java @@ -0,0 +1,3 @@ +module com.baeldung.student.model{ + exports com.baeldung.student.model; +} \ No newline at end of file diff --git a/core-java-9/src/modules/com.baeldung.student.service.dbimpl/com/baeldung/student/service/dbimpl/StudentDbService.java b/core-java-9/src/modules/com.baeldung.student.service.dbimpl/com/baeldung/student/service/dbimpl/StudentDbService.java new file mode 100644 index 000000000000..2519da085b5a --- /dev/null +++ b/core-java-9/src/modules/com.baeldung.student.service.dbimpl/com/baeldung/student/service/dbimpl/StudentDbService.java @@ -0,0 +1,30 @@ +package com.baeldung.student.service.dbimpl; + +import com.baeldung.student.service.StudentService; +import com.baeldung.student.model.Student; +import java.util.logging.*; + +public class StudentDbService implements StudentService { + + private static Logger logger = Logger.getLogger("StudentDbService"); + + public String create(Student student) { + logger.log(Level.INFO, "Creating student in DB..."); + return student.getRegistrationId(); + } + + public Student read(String registrationId) { + logger.log(Level.INFO, "Reading student from DB..."); + return new Student(); + } + + public Student update(Student student) { + logger.log(Level.INFO, "Updating sutdent in DB..."); + return student; + } + + public String delete(String registrationId) { + logger.log(Level.INFO, "Deleteing sutdent in DB..."); + return registrationId; + } +} \ No newline at end of file diff --git a/core-java-9/src/modules/com.baeldung.student.service.dbimpl/module-info.java b/core-java-9/src/modules/com.baeldung.student.service.dbimpl/module-info.java new file mode 100644 index 000000000000..96a453ea6b7e --- /dev/null +++ b/core-java-9/src/modules/com.baeldung.student.service.dbimpl/module-info.java @@ -0,0 +1,5 @@ +module com.baeldung.student.service.dbimpl{ + requires transitive com.baeldung.student.service; + exports com.baeldung.student.service.dbimpl; + requires java.logging; +} \ No newline at end of file diff --git a/core-java-9/src/modules/com.baeldung.student.service/com/baeldung/student/service/StudentService.java b/core-java-9/src/modules/com.baeldung.student.service/com/baeldung/student/service/StudentService.java new file mode 100644 index 000000000000..6076bf12e362 --- /dev/null +++ b/core-java-9/src/modules/com.baeldung.student.service/com/baeldung/student/service/StudentService.java @@ -0,0 +1,14 @@ +package com.baeldung.student.service; + +import com.baeldung.student.model.Student; + +public interface StudentService { + + public String create(Student student); + + public Student read(String registrationId); + + public Student update(Student student); + + public String delete(String registrationId); +} \ No newline at end of file diff --git a/core-java-9/src/modules/com.baeldung.student.service/module-info.java b/core-java-9/src/modules/com.baeldung.student.service/module-info.java new file mode 100644 index 000000000000..5de9e583482b --- /dev/null +++ b/core-java-9/src/modules/com.baeldung.student.service/module-info.java @@ -0,0 +1,4 @@ +module com.baeldung.student.service{ + requires transitive com.baeldung.student.model; + exports com.baeldung.student.service; +} \ No newline at end of file diff --git a/core-java-9/src/test/java/com/baeldung/java9/Java9OptionalsStreamTest.java b/core-java-9/src/test/java/com/baeldung/java9/Java9OptionalsStreamTest.java index 121c17a86063..a6060b1a9d2a 100644 --- a/core-java-9/src/test/java/com/baeldung/java9/Java9OptionalsStreamTest.java +++ b/core-java-9/src/test/java/com/baeldung/java9/Java9OptionalsStreamTest.java @@ -1,4 +1,4 @@ -package com.baeldung.java8; +package com.baeldung.java9; import static org.junit.Assert.assertEquals; @@ -8,7 +8,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import org.junit.Before; import org.junit.Test; public class Java9OptionalsStreamTest { diff --git a/core-java-9/src/test/java/com/baeldung/java9/concurrent/future/CompletableFutureTest.java b/core-java-9/src/test/java/com/baeldung/java9/concurrent/future/CompletableFutureTest.java new file mode 100644 index 000000000000..b71c21117788 --- /dev/null +++ b/core-java-9/src/test/java/com/baeldung/java9/concurrent/future/CompletableFutureTest.java @@ -0,0 +1,74 @@ +package com.baeldung.java9.concurrent.future; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import org.junit.Test; + +public class CompletableFutureTest { + @Test + public void testDelay () throws Exception { + Object input = new Object(); + CompletableFuture future = new CompletableFuture<>(); + future.completeAsync(() -> input, CompletableFuture.delayedExecutor(1, TimeUnit.SECONDS)); + + Thread.sleep(100); + + assertFalse(future.isDone()); + + Thread.sleep(1000); + assertTrue(future.isDone()); + assertSame(input, future.get()); + } + + @Test + public void testTimeoutTriggered () throws Exception { + CompletableFuture future = new CompletableFuture<>(); + future.orTimeout(1, TimeUnit.SECONDS); + + Thread.sleep(1100); + + assertTrue(future.isDone()); + + try { + future.get(); + } catch (ExecutionException e) { + assertTrue(e.getCause() instanceof TimeoutException); + } + } + + @Test + public void testTimeoutNotTriggered () throws Exception { + Object input = new Object(); + CompletableFuture future = new CompletableFuture<>(); + + future.orTimeout(1, TimeUnit.SECONDS); + + Thread.sleep(100); + + future.complete(input); + + Thread.sleep(1000); + + assertTrue(future.isDone()); + assertSame(input, future.get()); + } + + + + @Test + public void completeOnTimeout () throws Exception { + Object input = new Object(); + CompletableFuture future = new CompletableFuture<>(); + future.completeOnTimeout(input, 1, TimeUnit.SECONDS); + + Thread.sleep(1100); + + assertTrue(future.isDone()); + assertSame(input, future.get()); + } +} diff --git a/core-java-9/src/test/java/com/baeldung/java9/reactive/BaeldungBatchSubscriberImplTest.java b/core-java-9/src/test/java/com/baeldung/java9/reactive/BaeldungBatchSubscriberImplTest.java new file mode 100644 index 000000000000..388d3efdd8af --- /dev/null +++ b/core-java-9/src/test/java/com/baeldung/java9/reactive/BaeldungBatchSubscriberImplTest.java @@ -0,0 +1,75 @@ +package com.baeldung.java9.reactive; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.SubmissionPublisher; +import java.util.concurrent.TimeUnit; +import java.util.stream.IntStream; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.Stopwatch; + +public class BaeldungBatchSubscriberImplTest { + + private static final int ITEM_SIZE = 10; + private SubmissionPublisher publisher; + private BaeldungBatchSubscriberImpl subscriber; + + @Before + public void initialize() { + this.publisher = new SubmissionPublisher(ForkJoinPool.commonPool(), 6); + this.subscriber = new BaeldungBatchSubscriberImpl(); + publisher.subscribe(subscriber); + } + + @Rule + public Stopwatch stopwatch = new Stopwatch() { + + }; + + @Test + public void testReactiveStreamCount() { + IntStream.range(0, ITEM_SIZE) + .forEach(item -> publisher.submit(item + "")); + publisher.close(); + + do { + // wait for subscribers to complete all processing. + try { + Thread.sleep(100); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } while (!subscriber.isCompleted()); + + int count = subscriber.getCounter(); + + assertEquals(ITEM_SIZE, count); + } + + @Test + public void testReactiveStreamTime() { + IntStream.range(0, ITEM_SIZE) + .forEach(item -> publisher.submit(item + "")); + publisher.close(); + + do { + // wait for subscribers to complete all processing. + try { + Thread.sleep(100); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } while (!subscriber.isCompleted()); + + // The runtime in seconds should be equal to the number of items in each batch. + assertTrue(stopwatch.runtime(TimeUnit.SECONDS) >= (ITEM_SIZE / subscriber.BUFFER_SIZE)); + } + +} diff --git a/core-java-9/src/test/java/com/baeldung/java9/reactive/BaeldungSubscriberImplTest.java b/core-java-9/src/test/java/com/baeldung/java9/reactive/BaeldungSubscriberImplTest.java new file mode 100644 index 000000000000..5638c0a431ec --- /dev/null +++ b/core-java-9/src/test/java/com/baeldung/java9/reactive/BaeldungSubscriberImplTest.java @@ -0,0 +1,100 @@ +package com.baeldung.java9.reactive; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.SubmissionPublisher; +import java.util.concurrent.TimeUnit; +import java.util.stream.IntStream; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.Stopwatch; + +public class BaeldungSubscriberImplTest { + + private static final int ITEM_SIZE = 10; + private SubmissionPublisher publisher; + private BaeldungSubscriberImpl subscriber; + + @Before + public void initialize() { + // create Publisher with max buffer capacity 3. + this.publisher = new SubmissionPublisher(ForkJoinPool.commonPool(), 3); + this.subscriber = new BaeldungSubscriberImpl(); + publisher.subscribe(subscriber); + } + + @Rule + public Stopwatch stopwatch = new Stopwatch() { + + }; + + @Test + public void testReactiveStreamCount() { + IntStream.range(0, ITEM_SIZE) + .forEach(item -> publisher.submit(item + "")); + publisher.close(); + + do { + // wait for subscribers to complete all processing. + try { + Thread.sleep(100); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } while (!subscriber.isCompleted()); + + int count = subscriber.getCounter(); + + assertEquals(ITEM_SIZE, count); + } + + @Test + public void testReactiveStreamTime() { + IntStream.range(0, ITEM_SIZE) + .forEach(item -> publisher.submit(item + "")); + publisher.close(); + + do { + // wait for subscribers to complete all processing. + try { + Thread.sleep(100); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } while (!subscriber.isCompleted()); + + // The runtime in seconds should be equal to the number of items. + assertTrue(stopwatch.runtime(TimeUnit.SECONDS) >= ITEM_SIZE); + } + + @Test + public void testReactiveStreamOffer() { + IntStream.range(0, ITEM_SIZE) + .forEach(item -> publisher.offer(item + "", (subscriber, string) -> { + // Returning false means this item will be dropped (no retry), if blocked. + return false; + })); + publisher.close(); + + do { + // wait for subscribers to complete all processing. + try { + Thread.sleep(100); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } while (!subscriber.isCompleted()); + + int count = subscriber.getCounter(); + // Because 10 items were offered and the buffer capacity was 3, few items will not be processed. + assertTrue(ITEM_SIZE > count); + } + +} diff --git a/core-java-9/src/test/java/com/baeldung/java9/stackwalker/StackWalkerDemoTest.java b/core-java-9/src/test/java/com/baeldung/java9/stackwalker/StackWalkerDemoTest.java new file mode 100644 index 000000000000..b523b7dfb2d6 --- /dev/null +++ b/core-java-9/src/test/java/com/baeldung/java9/stackwalker/StackWalkerDemoTest.java @@ -0,0 +1,16 @@ +package com.baeldung.java9.stackwalker; + +import org.junit.Test; + +public class StackWalkerDemoTest { + + @Test + public void giveStalkWalker_whenWalkingTheStack_thenShowStackFrames() { + new StackWalkerDemo().methodOne(); + } + + @Test + public void giveStalkWalker_whenInvokingFindCaller_thenFindCallingClass() { + new StackWalkerDemo().findCaller(); + } +} diff --git a/core-java/.gitignore b/core-java/.gitignore index 251a8755bd17..2a03a0f72eb2 100644 --- a/core-java/.gitignore +++ b/core-java/.gitignore @@ -1,11 +1,14 @@ *.class +0.* + #folders# /target /neoDb* /data /src/main/webapp/WEB-INF/classes */META-INF/* +.resourceCache # Packaged files # *.jar diff --git a/core-java/0.5633433244738808 b/core-java/0.5633433244738808 deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/core-java/0.5967303215007616 b/core-java/0.5967303215007616 deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/core-java/0.6256429734439612 b/core-java/0.6256429734439612 deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/core-java/0.04832801936270381 b/core-java/0.8260098203820962 similarity index 100% rename from core-java/0.04832801936270381 rename to core-java/0.8260098203820962 diff --git a/core-java/0.9252611327674576 b/core-java/0.9252611327674576 deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/core-java/0.9799201796740292 b/core-java/0.9799201796740292 deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/core-java/README.md b/core-java/README.md index a34908d8ae66..910e0fbbece4 100644 --- a/core-java/README.md +++ b/core-java/README.md @@ -11,6 +11,9 @@ - [Converting between a List and a Set in Java](http://www.baeldung.com/convert-list-to-set-and-set-to-list) - [Convert a Map to an Array, List or Set in Java](http://www.baeldung.com/convert-map-values-to-array-list-set) - [Java – Write to File](http://www.baeldung.com/java-write-to-file) +- [Java - Convert File to InputStream](http://www.baeldung.com/convert-file-to-input-stream) +- [Java – Random Long, Float, Integer and Double](http://www.baeldung.com/java-generate-random-long-float-integer-double) +- [Java – Generate Random String](http://www.baeldung.com/java-random-string) - [Java Scanner](http://www.baeldung.com/java-scanner) - [Java Timer](http://www.baeldung.com/java-timer-and-timertask) - [Java – Byte Array to Writer](http://www.baeldung.com/java-convert-byte-array-to-writer) @@ -79,3 +82,19 @@ - [The Java HashMap Under the Hood](http://www.baeldung.com/java-hashmap) - [A Guide to LinkedHashMap in Java](http://www.baeldung.com/java-linked-hashmap) - [A Guide to TreeMap in Java](http://www.baeldung.com/java-treemap) +- [A Quick JUnit vs TestNG Comparison](http://www.baeldung.com/junit-vs-testng) +- [Finding Max/Min of a List or Collection](http://www.baeldung.com/java-collection-min-max) +- [Guide to java.util.concurrent.Locks](http://www.baeldung.com/java-concurrent-locks) +- [Java Primitive Conversions](http://www.baeldung.com/java-primitive-conversions) +- [Java Money and the Currency API](http://www.baeldung.com/java-money-and-currency) +- [Guide to Java 8 Comparator.comparing()](http://www.baeldung.com/java-8-comparator-comparing) +- [Avoiding ConcurrentModificationException when iterating and removing](http://www.baeldung.com/avoiding-concurrentmodificationexception-when-iterating-and-removing) +- [Removing all nulls from a List in Java](http://www.baeldung.com/java-remove-nulls-from-list) +- [Removing all duplicates from a List in Java](http://www.baeldung.com/java-remove-duplicates-from-list) +- [An Introduction to ThreadLocal in Java](http://www.baeldung.com/java-threadlocal) +- [Using Math.pow in Java](http://www.baeldung.com/java-math-pow) +- [Converting Strings to Enums in Java](http://www.baeldung.com/java-string-to-enum) +- [Flattening Nested Collections in Java](http://www.baeldung.com/java-flatten-nested-collections) +- [Quick Guide to the Java StringTokenizer](http://www.baeldung.com/java-stringtokenizer) +- [JVM Log Forging](http://www.baeldung.com/jvm-log-forging) +- [Guide to sun.misc.Unsafe](http://www.baeldung.com/java-unsafe) diff --git a/core-java/pom.xml b/core-java/pom.xml index b2c59989f119..b4741d5b80c3 100644 --- a/core-java/pom.xml +++ b/core-java/pom.xml @@ -92,11 +92,6 @@ ${org.slf4j.version} - - org.slf4j - log4j-over-slf4j - ${org.slf4j.version} - org.projectlombok lombok @@ -165,6 +160,24 @@ commons-codec ${commons-codec.version} + + + org.javamoney + moneta + 1.1 + + + + org.owasp.esapi + esapi + 2.1.0.1 + + + commons-logging + commons-logging + + + @@ -192,7 +205,6 @@ org.apache.maven.plugins maven-surefire-plugin - ${maven-surefire-plugin.version} **/*IntegrationTest.java @@ -244,6 +256,7 @@ single + ${project.basedir} org.baeldung.executable.ExecutableMavenJar @@ -385,4 +398,4 @@ - \ No newline at end of file + diff --git a/core-java/src/main/java/com/baeldung/algorithms/ga/ant_colony/AntColonyOptimization.java b/core-java/src/main/java/com/baeldung/algorithms/ga/ant_colony/AntColonyOptimization.java deleted file mode 100644 index e46ac77e8434..000000000000 --- a/core-java/src/main/java/com/baeldung/algorithms/ga/ant_colony/AntColonyOptimization.java +++ /dev/null @@ -1,212 +0,0 @@ -package com.baeldung.algorithms.ga.ant_colony; - -import java.util.Arrays; -import java.util.Random; - -public class AntColonyOptimization { - - private double c = 1.0; - private double alpha = 1; - private double beta = 5; - private double evaporation = 0.5; - private double Q = 500; - private double antFactor = 0.8; - private double randomFactor = 0.01; - - private int maxIterations = 1000; - - public int numberOfCities; - public int numberOfAnts; - private double graph[][]; - private double trails[][]; - private Ant ants[]; - private Random random = new Random(); - private double probabilities[]; - - private int currentIndex; - - public int[] bestTourOrder; - public double bestTourLength; - - public AntColonyOptimization(int noOfCities) { - graph = generateRandomMatrix(noOfCities); - numberOfCities = graph.length; - numberOfAnts = (int) (numberOfCities * antFactor); - - trails = new double[numberOfCities][numberOfCities]; - probabilities = new double[numberOfCities]; - ants = new Ant[numberOfAnts]; - for (int j = 0; j < numberOfAnts; j++) { - ants[j] = new Ant(numberOfCities); - } - } - - /** - * Generate initial solution - * @param n - * @return - */ - public double[][] generateRandomMatrix(int n) { - double[][] randomMatrix = new double[n][n]; - random.setSeed(System.currentTimeMillis()); - for (int i = 0; i < n; i++) { - for (int j = 0; j < n; j++) { - Integer r = random.nextInt(100) + 1; - randomMatrix[i][j] = Math.abs(r); - } - } - return randomMatrix; - } - - /** - * Perform ant optimization - * @return - */ - public int[] startAntOptimization() { - int[] finalResult = null; - for (int i = 1; i <= 3; i++) { - System.out.println("Attempt #" + i); - finalResult = solve(); - } - return finalResult; - } - - /** - * Use this method to run the main logic - * @return - */ - private int[] solve() { - setupAnts(); - clearTrails(); - int iteration = 0; - while (iteration < maxIterations) { - moveAnts(); - updateTrails(); - updateBest(); - iteration++; - } - System.out.println("Best tour length: " + (bestTourLength - numberOfCities)); - System.out.println("Best tour order: " + Arrays.toString(bestTourOrder)); - return bestTourOrder.clone(); - } - - /** - * Prepare ants for the simulation - */ - private void setupAnts() { - currentIndex = -1; - for (int i = 0; i < numberOfAnts; i++) { - ants[i].clear(); - ants[i].visitCity(currentIndex, random.nextInt(numberOfCities)); - } - currentIndex++; - } - - /** - * At each iteration, move ants - */ - private void moveAnts() { - while (currentIndex < numberOfCities - 1) { - for (Ant a : ants) - a.visitCity(currentIndex, selectNextCity(a)); - currentIndex++; - } - } - - /** - * Select next city for each ant - * @param ant - * @return - */ - private int selectNextCity(Ant ant) { - if (random.nextDouble() < randomFactor) { - int t = random.nextInt(numberOfCities - currentIndex); - int j = -1; - for (int i = 0; i < numberOfCities; i++) { - if (!ant.visited(i)) { - j++; - } - if (j == t) { - return i; - } - } - } - calculateProbabilities(ant); - double r = random.nextDouble(); - double total = 0; - for (int i = 0; i < numberOfCities; i++) { - total += probabilities[i]; - if (total >= r) { - return i; - } - } - - throw new RuntimeException("There are no other cities"); - } - - /** - * Calculate the next city picks probabilites - * @param ant - */ - private void calculateProbabilities(Ant ant) { - int i = ant.trail[currentIndex]; - double pheromone = 0.0; - for (int l = 0; l < numberOfCities; l++) { - if (!ant.visited(l)) { - pheromone += Math.pow(trails[i][l], alpha) * Math.pow(1.0 / graph[i][l], beta); - } - } - for (int j = 0; j < numberOfCities; j++) { - if (ant.visited(j)) { - probabilities[j] = 0.0; - } else { - double numerator = Math.pow(trails[i][j], alpha) * Math.pow(1.0 / graph[i][j], beta); - probabilities[j] = numerator / pheromone; - } - } - } - - /** - * Update trails that ants used - */ - private void updateTrails() { - for (int i = 0; i < numberOfCities; i++) { - for (int j = 0; j < numberOfCities; j++) { - trails[i][j] *= evaporation; - } - } - for (Ant a : ants) { - double contribution = Q / a.trailLength(graph); - for (int i = 0; i < numberOfCities - 1; i++) { - trails[a.trail[i]][a.trail[i + 1]] += contribution; - } - trails[a.trail[numberOfCities - 1]][a.trail[0]] += contribution; - } - } - - /** - * Update the best solution - */ - private void updateBest() { - if (bestTourOrder == null) { - bestTourOrder = ants[0].trail; - bestTourLength = ants[0].trailLength(graph); - } - for (Ant a : ants) { - if (a.trailLength(graph) < bestTourLength) { - bestTourLength = a.trailLength(graph); - bestTourOrder = a.trail.clone(); - } - } - } - - /** - * Clear trails after simulation - */ - private void clearTrails() { - for (int i = 0; i < numberOfCities; i++) - for (int j = 0; j < numberOfCities; j++) - trails[i][j] = c; - } - -} diff --git a/core-java/src/main/java/com/baeldung/arraycopy/model/Address.java b/core-java/src/main/java/com/baeldung/arraycopy/model/Address.java new file mode 100644 index 000000000000..43c6d77fe6cb --- /dev/null +++ b/core-java/src/main/java/com/baeldung/arraycopy/model/Address.java @@ -0,0 +1,61 @@ +package com.baeldung.arraycopy.model; + +public class Address implements Cloneable { + private String country; + private String state; + private String city; + private String street; + private String zipcode; + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getStreet() { + return street; + } + + public void setStreet(String street) { + this.street = street; + } + + public String getZipcode() { + return zipcode; + } + + public void setZipcode(String zipcode) { + this.zipcode = zipcode; + } + + @Override + protected Object clone() throws CloneNotSupportedException { + super.clone(); + Address address = new Address(); + address.setCity(this.city); + address.setCountry(this.country); + address.setState(this.state); + address.setStreet(this.street); + address.setZipcode(this.zipcode); + return address; + } +} diff --git a/core-java/src/main/java/com/baeldung/arraycopy/model/Employee.java b/core-java/src/main/java/com/baeldung/arraycopy/model/Employee.java new file mode 100644 index 000000000000..757a8f8ec167 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/arraycopy/model/Employee.java @@ -0,0 +1,25 @@ +package com.baeldung.arraycopy.model; + +import java.io.Serializable; + +public class Employee implements Serializable { + private static final long serialVersionUID = -2454619097207585825L; + private int id; + private String name; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/core-java/src/main/java/com/baeldung/logforging/LogForgingDemo.java b/core-java/src/main/java/com/baeldung/logforging/LogForgingDemo.java new file mode 100644 index 000000000000..0a9a5df850fb --- /dev/null +++ b/core-java/src/main/java/com/baeldung/logforging/LogForgingDemo.java @@ -0,0 +1,28 @@ +package com.baeldung.logforging; + +import org.owasp.esapi.ESAPI; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class LogForgingDemo { + + private final Logger logger = LoggerFactory.getLogger(LogForgingDemo.class); + + public void addLog(String amount) { + logger.info("Amount credited = {}", amount); + } + + public static void main(String[] args) { + LogForgingDemo demo = new LogForgingDemo(); + demo.addLog("300"); + demo.addLog("300 \n\nweb - 2017-04-12 17:47:08,957 [main] INFO Amount reversed successfully"); + demo.addLog(encode("300 \n\nweb - 2017-04-12 17:47:08,957 [main] INFO Amount reversed successfully")); + } + + public static String encode(String message) { + message = message.replace('\n', '_').replace('\r', '_').replace('\t', '_'); + message = ESAPI.encoder().encodeForHTML(message); + return message; + } + +} diff --git a/core-java/src/main/java/com/baeldung/money/JavaMoney.java b/core-java/src/main/java/com/baeldung/money/JavaMoney.java new file mode 100644 index 000000000000..3171d226edc5 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/money/JavaMoney.java @@ -0,0 +1,151 @@ +package com.baeldung.money; + +import java.util.Locale; +import java.util.logging.Logger; + +import javax.money.CurrencyUnit; +import javax.money.Monetary; +import javax.money.MonetaryAmount; +import javax.money.UnknownCurrencyException; +import javax.money.convert.ConversionQueryBuilder; +import javax.money.convert.CurrencyConversion; +import javax.money.convert.MonetaryConversions; +import javax.money.format.AmountFormatQueryBuilder; +import javax.money.format.MonetaryAmountFormat; +import javax.money.format.MonetaryFormats; + +import org.javamoney.moneta.FastMoney; +import org.javamoney.moneta.Money; +import org.javamoney.moneta.format.CurrencyStyle; + +public class JavaMoney { + final static Logger LOGGER = Logger.getLogger(JavaMoney.class.getName()); + CurrencyUnit USD; + MonetaryAmount fstAmtUSD; + MonetaryAmount fstAmtEUR; + MonetaryAmount oneDolar; + MonetaryAmount moneyof; + MonetaryAmount fastmoneyof; + MonetaryAmount roundEUR; + MonetaryAmount calcAmtUSD; + MonetaryAmount[] monetaryAmounts; + MonetaryAmount sumAmtCHF; + MonetaryAmount calcMoneyFastMoney; + MonetaryAmount convertedAmountEURtoUSD; + MonetaryAmount convertedAmountEURtoUSD2; + MonetaryAmount convertedAmountUSDtoEUR; + MonetaryAmount convertedAmountUSDtoEUR2; + MonetaryAmount multiplyAmount; + MonetaryAmount divideAmount; + MonetaryAmount oneDivThree; + CurrencyConversion convEUR; + CurrencyConversion convUSD; + CurrencyConversion conversionUSD; + CurrencyConversion conversionEUR; + MonetaryAmount oneEuro; + MonetaryAmountFormat formatUSD; + MonetaryAmountFormat customFormat; + String usFormatted; + String customFormatted; + + public JavaMoney() { + USD = Monetary.getCurrency("USD"); + fstAmtUSD = Monetary.getDefaultAmountFactory().setCurrency(USD).setNumber(200.50).create(); + fstAmtEUR = Monetary.getDefaultAmountFactory().setCurrency("EUR").setNumber(1.30473908).create(); + oneDolar = Monetary.getDefaultAmountFactory().setCurrency("USD").setNumber(1).create(); + moneyof = Money.of(12, USD); + fastmoneyof = FastMoney.of(2, USD); + + LOGGER.info("First Amount in USD : " + fstAmtUSD); + LOGGER.info("First Amount in EUR : " + fstAmtEUR); + LOGGER.info("One Dolar : " + oneDolar); + LOGGER.info("MoneyOf : " + moneyof); + LOGGER.info("FastMoneyOf : " + fastmoneyof); + + try{ + @SuppressWarnings("unused") + CurrencyUnit AAA = Monetary.getCurrency("AAA"); + } catch (UnknownCurrencyException e) { + LOGGER.severe("Unknown Currency"); + } + + roundEUR = fstAmtEUR.with(Monetary.getDefaultRounding()); + + LOGGER.info("Rounded EUR : " + roundEUR); + + calcAmtUSD = Money.of(1, "USD").subtract(fstAmtUSD); + + LOGGER.info("Substracting amounts : " + calcAmtUSD); + + calcMoneyFastMoney = moneyof.subtract(fastmoneyof); + + LOGGER.info("Money & FastMoney operations : " + calcMoneyFastMoney); + + monetaryAmounts = + new MonetaryAmount[] { + Money.of(100, "CHF"), + Money.of(10.20, "CHF"), + Money.of(1.15, "CHF"), }; + sumAmtCHF = Money.of(0, "CHF"); + for (MonetaryAmount monetaryAmount : monetaryAmounts) { + sumAmtCHF = sumAmtCHF.add(monetaryAmount); + } + + LOGGER.info("Adding amounts : " + sumAmtCHF); + + multiplyAmount = oneDolar.multiply(0.25); + LOGGER.info("Multiply Amount : " + multiplyAmount); + + divideAmount = oneDolar.divide(0.25); + LOGGER.info("Divide Amount : " + divideAmount); + + try{ + oneDivThree = oneDolar.divide(3); + }catch (ArithmeticException e) { + LOGGER.severe("One divide by Three is an infinite number"); + } + + convEUR = MonetaryConversions.getConversion(ConversionQueryBuilder.of().setTermCurrency("EUR").build()); + convUSD = MonetaryConversions.getConversion(ConversionQueryBuilder.of().setTermCurrency(USD).build()); + + conversionUSD = MonetaryConversions.getConversion("USD"); + conversionEUR = MonetaryConversions.getConversion("EUR"); + + convertedAmountEURtoUSD = fstAmtEUR.with(conversionUSD); + convertedAmountEURtoUSD2 = fstAmtEUR.with(convUSD); + convertedAmountUSDtoEUR = oneDolar.with(conversionEUR); + convertedAmountUSDtoEUR2 = oneDolar.with(convEUR); + LOGGER.info("C1 - " + convertedAmountEURtoUSD); + LOGGER.info("C2 - " + convertedAmountEURtoUSD2); + LOGGER.info("One Euro -> " + convertedAmountUSDtoEUR); + LOGGER.info("One Euro2 -> " + convertedAmountUSDtoEUR2); + + oneEuro = Money.of(1, "EUR"); + + if (oneEuro.equals(FastMoney.of(1, "EUR"))) { + LOGGER.info("Money == FastMoney"); + } else { + LOGGER.info("Money != FastMoney"); + } + + if (oneDolar.equals(Money.of(1, "USD"))) { + LOGGER.info("Factory == Money"); + } else { + LOGGER.info("Factory != Money"); + } + + formatUSD = MonetaryFormats.getAmountFormat(Locale.US); + usFormatted = formatUSD.format(oneDolar); + LOGGER.info("One dolar standard formatted : " + usFormatted); + + customFormat = MonetaryFormats.getAmountFormat(AmountFormatQueryBuilder.of(Locale.US).set(CurrencyStyle.NAME).set("pattern", "00000.00 ¤").build()); + customFormatted = customFormat.format(oneDolar); + LOGGER.info("One dolar custom formatted : " + customFormatted); + } + + public static void main(String[] args) { + @SuppressWarnings("unused") + JavaMoney java9Money = new JavaMoney(); + } + +} diff --git a/core-java/src/main/java/com/baeldung/pow/PowerExample.java b/core-java/src/main/java/com/baeldung/pow/PowerExample.java new file mode 100644 index 000000000000..5be5376e6a79 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/pow/PowerExample.java @@ -0,0 +1,19 @@ +package com.baeldung.pow; + +import java.text.DecimalFormat; + +public class PowerExample { + + public static void main(String[] args) { + + int intResult = (int) Math.pow(2, 3); + System.out.println("Math.pow(2, 3) = " + intResult); + + double dblResult = Math.pow(4.2, 3); + System.out.println("Math.pow(4.2, 3) = " + Math.pow(4.2, 3)); + + DecimalFormat df = new DecimalFormat(".00"); + System.out.println("Math.pow(4.2, 3) rounded = " + df.format(dblResult)); + + } +} diff --git a/core-java/src/main/java/com/baeldung/stringtokenizer/MyTokenizer.java b/core-java/src/main/java/com/baeldung/stringtokenizer/MyTokenizer.java new file mode 100644 index 000000000000..130218acc269 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/stringtokenizer/MyTokenizer.java @@ -0,0 +1,52 @@ +package com.baeldung.stringtokenizer; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.StringTokenizer; +import java.util.stream.Collectors; + +public class MyTokenizer { + + public List getTokens(String str) { + List tokens = new ArrayList<>(); + // StringTokenizer tokenizer = new StringTokenizer( str ); + StringTokenizer tokenizer = new StringTokenizer(str, ","); + // StringTokenizer tokenizer = new StringTokenizer( str , "," , true ); + while (tokenizer.hasMoreElements()) { + tokens.add(tokenizer.nextToken()); + // tokens.add( tokenizer.nextToken("e") ); + } + int tokenLength = tokens.size(); + return tokens; + } + + public List getTokensWithCollection(String str) { + return Collections + .list(new StringTokenizer(str, ",")) + .stream() + .map(token -> (String) token) + .collect(Collectors.toList()); + } + + public List getTokensFromFile(String path, String delim) { + List tokens = new ArrayList<>(); + String currLine; + StringTokenizer tokenizer; + try (BufferedReader br = new BufferedReader(new InputStreamReader(MyTokenizer.class.getResourceAsStream("/" + path)))) { + while ((currLine = br.readLine()) != null) { + tokenizer = new StringTokenizer(currLine, delim); + while (tokenizer.hasMoreElements()) { + tokens.add(tokenizer.nextToken()); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return tokens; + } + +} diff --git a/core-java/src/main/java/com/baeldung/threadlocal/Context.java b/core-java/src/main/java/com/baeldung/threadlocal/Context.java new file mode 100644 index 000000000000..241fb2f1e023 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/threadlocal/Context.java @@ -0,0 +1,17 @@ +package com.baeldung.threadlocal; + + +public class Context { + private final String userName; + + public Context(String userName) { + this.userName = userName; + } + + @Override + public String toString() { + return "Context{" + + "userNameSecret='" + userName + '\'' + + '}'; + } +} diff --git a/core-java/src/main/java/com/baeldung/threadlocal/SharedMapWithUserContext.java b/core-java/src/main/java/com/baeldung/threadlocal/SharedMapWithUserContext.java new file mode 100644 index 000000000000..e5854e218a27 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/threadlocal/SharedMapWithUserContext.java @@ -0,0 +1,21 @@ +package com.baeldung.threadlocal; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class SharedMapWithUserContext implements Runnable { + public final static Map userContextPerUserId = new ConcurrentHashMap<>(); + private final Integer userId; + private UserRepository userRepository = new UserRepository(); + + public SharedMapWithUserContext(Integer userId) { + this.userId = userId; + } + + @Override + public void run() { + String userName = userRepository.getUserNameForUserId(userId); + userContextPerUserId.put(userId, new Context(userName)); + } +} diff --git a/core-java/src/main/java/com/baeldung/threadlocal/ThreadLocalWithUserContext.java b/core-java/src/main/java/com/baeldung/threadlocal/ThreadLocalWithUserContext.java new file mode 100644 index 000000000000..0d6a7e5572e8 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/threadlocal/ThreadLocalWithUserContext.java @@ -0,0 +1,20 @@ +package com.baeldung.threadlocal; + + +public class ThreadLocalWithUserContext implements Runnable { + private static final ThreadLocal userContext = new ThreadLocal<>(); + private final Integer userId; + private UserRepository userRepository = new UserRepository(); + + public ThreadLocalWithUserContext(Integer userId) { + this.userId = userId; + } + + + @Override + public void run() { + String userName = userRepository.getUserNameForUserId(userId); + userContext.set(new Context(userName)); + System.out.println("thread context for given userId: " + userId + " is: " + userContext.get()); + } +} diff --git a/core-java/src/main/java/com/baeldung/threadlocal/UserRepository.java b/core-java/src/main/java/com/baeldung/threadlocal/UserRepository.java new file mode 100644 index 000000000000..3fe76f75c070 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/threadlocal/UserRepository.java @@ -0,0 +1,10 @@ +package com.baeldung.threadlocal; + +import java.util.UUID; + + +public class UserRepository { + public String getUserNameForUserId(Integer userId) { + return UUID.randomUUID().toString(); + } +} diff --git a/core-java/src/main/java/com/baeldung/transferqueue/Consumer.java b/core-java/src/main/java/com/baeldung/transferqueue/Consumer.java new file mode 100644 index 000000000000..a498d080415b --- /dev/null +++ b/core-java/src/main/java/com/baeldung/transferqueue/Consumer.java @@ -0,0 +1,36 @@ +package com.baeldung.transferqueue; + +import java.util.concurrent.TransferQueue; +import java.util.concurrent.atomic.AtomicInteger; + +public class Consumer implements Runnable { + private final TransferQueue transferQueue; + private final String name; + private final int numberOfMessagesToConsume; + public final AtomicInteger numberOfConsumedMessages = new AtomicInteger(); + + public Consumer(TransferQueue transferQueue, String name, int numberOfMessagesToConsume) { + this.transferQueue = transferQueue; + this.name = name; + this.numberOfMessagesToConsume = numberOfMessagesToConsume; + } + + @Override + public void run() { + for (int i = 0; i < numberOfMessagesToConsume; i++) { + try { + System.out.println("Consumer: " + name + " is waiting to take element..."); + String element = transferQueue.take(); + longProcessing(element); + System.out.println("Consumer: " + name + " received element: " + element); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + private void longProcessing(String element) throws InterruptedException { + numberOfConsumedMessages.incrementAndGet(); + Thread.sleep(500); + } +} \ No newline at end of file diff --git a/core-java/src/main/java/com/baeldung/transferqueue/Producer.java b/core-java/src/main/java/com/baeldung/transferqueue/Producer.java new file mode 100644 index 000000000000..c9edc69e33cf --- /dev/null +++ b/core-java/src/main/java/com/baeldung/transferqueue/Producer.java @@ -0,0 +1,36 @@ +package com.baeldung.transferqueue; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TransferQueue; +import java.util.concurrent.atomic.AtomicInteger; + +public class Producer implements Runnable { + private final TransferQueue transferQueue; + private final String name; + private final Integer numberOfMessagesToProduce; + public final AtomicInteger numberOfProducedMessages = new AtomicInteger(); + + public Producer(TransferQueue transferQueue, String name, Integer numberOfMessagesToProduce) { + this.transferQueue = transferQueue; + this.name = name; + this.numberOfMessagesToProduce = numberOfMessagesToProduce; + } + + @Override + public void run() { + for (int i = 0; i < numberOfMessagesToProduce; i++) { + try { + System.out.println("Producer: " + name + " is waiting to transfer..."); + boolean added = transferQueue.tryTransfer("A" + i, 4000, TimeUnit.MILLISECONDS); + if (!added) { + System.out.println("can not add an element due to the timeout"); + } else { + numberOfProducedMessages.incrementAndGet(); + System.out.println("Producer: " + name + " transferred element: A" + i); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } +} \ No newline at end of file diff --git a/core-java/src/main/resources/ESAPI.properties b/core-java/src/main/resources/ESAPI.properties new file mode 100644 index 000000000000..24dcaa9dfa4b --- /dev/null +++ b/core-java/src/main/resources/ESAPI.properties @@ -0,0 +1,453 @@ +# +# OWASP Enterprise Security API (ESAPI) Properties file -- PRODUCTION Version +# +# This file is part of the Open Web Application Security Project (OWASP) +# Enterprise Security API (ESAPI) project. For details, please see +# http://www.owasp.org/index.php/ESAPI. +# +# Copyright (c) 2008,2009 - The OWASP Foundation +# +# DISCUSS: This may cause a major backwards compatibility issue, etc. but +# from a name space perspective, we probably should have prefaced +# all the property names with ESAPI or at least OWASP. Otherwise +# there could be problems is someone loads this properties file into +# the System properties. We could also put this file into the +# esapi.jar file (perhaps as a ResourceBundle) and then allow an external +# ESAPI properties be defined that would overwrite these defaults. +# That keeps the application's properties relatively simple as usually +# they will only want to override a few properties. If looks like we +# already support multiple override levels of this in the +# DefaultSecurityConfiguration class, but I'm suggesting placing the +# defaults in the esapi.jar itself. That way, if the jar is signed, +# we could detect if those properties had been tampered with. (The +# code to check the jar signatures is pretty simple... maybe 70-90 LOC, +# but off course there is an execution penalty (similar to the way +# that the separate sunjce.jar used to be when a class from it was +# first loaded). Thoughts? +############################################################################### +# +# WARNING: Operating system protection should be used to lock down the .esapi +# resources directory and all the files inside and all the directories all the +# way up to the root directory of the file system. Note that if you are using +# file-based implementations, that some files may need to be read-write as they +# get updated dynamically. +# +# Before using, be sure to update the MasterKey and MasterSalt as described below. +# N.B.: If you had stored data that you have previously encrypted with ESAPI 1.4, +# you *must* FIRST decrypt it using ESAPI 1.4 and then (if so desired) +# re-encrypt it with ESAPI 2.0. If you fail to do this, you will NOT be +# able to decrypt your data with ESAPI 2.0. +# +# YOU HAVE BEEN WARNED!!! More details are in the ESAPI 2.0 Release Notes. +# +#=========================================================================== +# ESAPI Configuration +# +# If true, then print all the ESAPI properties set here when they are loaded. +# If false, they are not printed. Useful to reduce output when running JUnit tests. +# If you need to troubleshoot a properties related problem, turning this on may help. +# This is 'false' in the src/test/resources/.esapi version. It is 'true' by +# default for reasons of backward compatibility with earlier ESAPI versions. +ESAPI.printProperties=true + +# ESAPI is designed to be easily extensible. You can use the reference implementation +# or implement your own providers to take advantage of your enterprise's security +# infrastructure. The functions in ESAPI are referenced using the ESAPI locator, like: +# +# String ciphertext = +# ESAPI.encryptor().encrypt("Secret message"); // Deprecated in 2.0 +# CipherText cipherText = +# ESAPI.encryptor().encrypt(new PlainText("Secret message")); // Preferred +# +# Below you can specify the classname for the provider that you wish to use in your +# application. The only requirement is that it implement the appropriate ESAPI interface. +# This allows you to switch security implementations in the future without rewriting the +# entire application. +# +# ExperimentalAccessController requires ESAPI-AccessControlPolicy.xml in .esapi directory +ESAPI.AccessControl=org.owasp.esapi.reference.DefaultAccessController +# FileBasedAuthenticator requires users.txt file in .esapi directory +ESAPI.Authenticator=org.owasp.esapi.reference.FileBasedAuthenticator +ESAPI.Encoder=org.owasp.esapi.reference.DefaultEncoder +ESAPI.Encryptor=org.owasp.esapi.reference.crypto.JavaEncryptor + +ESAPI.Executor=org.owasp.esapi.reference.DefaultExecutor +ESAPI.HTTPUtilities=org.owasp.esapi.reference.DefaultHTTPUtilities +ESAPI.IntrusionDetector=org.owasp.esapi.reference.DefaultIntrusionDetector +# Log4JFactory Requires log4j.xml or log4j.properties in classpath - http://www.laliluna.de/log4j-tutorial.html +ESAPI.Logger=org.owasp.esapi.reference.Log4JLogFactory +#ESAPI.Logger=org.owasp.esapi.reference.JavaLogFactory +ESAPI.Randomizer=org.owasp.esapi.reference.DefaultRandomizer +ESAPI.Validator=org.owasp.esapi.reference.DefaultValidator + +#=========================================================================== +# ESAPI Authenticator +# +Authenticator.AllowedLoginAttempts=3 +Authenticator.MaxOldPasswordHashes=13 +Authenticator.UsernameParameterName=username +Authenticator.PasswordParameterName=password +# RememberTokenDuration (in days) +Authenticator.RememberTokenDuration=14 +# Session Timeouts (in minutes) +Authenticator.IdleTimeoutDuration=20 +Authenticator.AbsoluteTimeoutDuration=120 + +#=========================================================================== +# ESAPI Encoder +# +# ESAPI canonicalizes input before validation to prevent bypassing filters with encoded attacks. +# Failure to canonicalize input is a very common mistake when implementing validation schemes. +# Canonicalization is automatic when using the ESAPI Validator, but you can also use the +# following code to canonicalize data. +# +# ESAPI.Encoder().canonicalize( "%22hello world"" ); +# +# Multiple encoding is when a single encoding format is applied multiple times. Allowing +# multiple encoding is strongly discouraged. +Encoder.AllowMultipleEncoding=false + +# Mixed encoding is when multiple different encoding formats are applied, or when +# multiple formats are nested. Allowing multiple encoding is strongly discouraged. +Encoder.AllowMixedEncoding=false + +# The default list of codecs to apply when canonicalizing untrusted data. The list should include the codecs +# for all downstream interpreters or decoders. For example, if the data is likely to end up in a URL, HTML, or +# inside JavaScript, then the list of codecs below is appropriate. The order of the list is not terribly important. +Encoder.DefaultCodecList=HTMLEntityCodec,PercentCodec,JavaScriptCodec + + +#=========================================================================== +# ESAPI Encryption +# +# The ESAPI Encryptor provides basic cryptographic functions with a simplified API. +# To get started, generate a new key using java -classpath esapi.jar org.owasp.esapi.reference.crypto.JavaEncryptor +# There is not currently any support for key rotation, so be careful when changing your key and salt as it +# will invalidate all signed, encrypted, and hashed data. +# +# WARNING: Not all combinations of algorithms and key lengths are supported. +# If you choose to use a key length greater than 128, you MUST download the +# unlimited strength policy files and install in the lib directory of your JRE/JDK. +# See http://java.sun.com/javase/downloads/index.jsp for more information. +# +# Backward compatibility with ESAPI Java 1.4 is supported by the two deprecated API +# methods, Encryptor.encrypt(String) and Encryptor.decrypt(String). However, whenever +# possible, these methods should be avoided as they use ECB cipher mode, which in almost +# all circumstances a poor choice because of it's weakness. CBC cipher mode is the default +# for the new Encryptor encrypt / decrypt methods for ESAPI Java 2.0. In general, you +# should only use this compatibility setting if you have persistent data encrypted with +# version 1.4 and even then, you should ONLY set this compatibility mode UNTIL +# you have decrypted all of your old encrypted data and then re-encrypted it with +# ESAPI 2.0 using CBC mode. If you have some reason to mix the deprecated 1.4 mode +# with the new 2.0 methods, make sure that you use the same cipher algorithm for both +# (256-bit AES was the default for 1.4; 128-bit is the default for 2.0; see below for +# more details.) Otherwise, you will have to use the new 2.0 encrypt / decrypt methods +# where you can specify a SecretKey. (Note that if you are using the 256-bit AES, +# that requires downloading the special jurisdiction policy files mentioned above.) +# +# ***** IMPORTANT: Do NOT forget to replace these with your own values! ***** +# To calculate these values, you can run: +# java -classpath esapi.jar org.owasp.esapi.reference.crypto.JavaEncryptor +# +Encryptor.MasterKey=tzfztf56ftv +Encryptor.MasterSalt=123456ztrewq + +# Provides the default JCE provider that ESAPI will "prefer" for its symmetric +# encryption and hashing. (That is it will look to this provider first, but it +# will defer to other providers if the requested algorithm is not implemented +# by this provider.) If left unset, ESAPI will just use your Java VM's current +# preferred JCE provider, which is generally set in the file +# "$JAVA_HOME/jre/lib/security/java.security". +# +# The main intent of this is to allow ESAPI symmetric encryption to be +# used with a FIPS 140-2 compliant crypto-module. For details, see the section +# "Using ESAPI Symmetric Encryption with FIPS 140-2 Cryptographic Modules" in +# the ESAPI 2.0 Symmetric Encryption User Guide, at: +# http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html +# However, this property also allows you to easily use an alternate JCE provider +# such as "Bouncy Castle" without having to make changes to "java.security". +# See Javadoc for SecurityProviderLoader for further details. If you wish to use +# a provider that is not known to SecurityProviderLoader, you may specify the +# fully-qualified class name of the JCE provider class that implements +# java.security.Provider. If the name contains a '.', this is interpreted as +# a fully-qualified class name that implements java.security.Provider. +# +# NOTE: Setting this property has the side-effect of changing it in your application +# as well, so if you are using JCE in your application directly rather than +# through ESAPI (you wouldn't do that, would you? ;-), it will change the +# preferred JCE provider there as well. +# +# Default: Keeps the JCE provider set to whatever JVM sets it to. +Encryptor.PreferredJCEProvider= + +# AES is the most widely used and strongest encryption algorithm. This +# should agree with your Encryptor.CipherTransformation property. +# By default, ESAPI Java 1.4 uses "PBEWithMD5AndDES" and which is +# very weak. It is essentially a password-based encryption key, hashed +# with MD5 around 1K times and then encrypted with the weak DES algorithm +# (56-bits) using ECB mode and an unspecified padding (it is +# JCE provider specific, but most likely "NoPadding"). However, 2.0 uses +# "AES/CBC/PKCSPadding". If you want to change these, change them here. +# Warning: This property does not control the default reference implementation for +# ESAPI 2.0 using JavaEncryptor. Also, this property will be dropped +# in the future. +# @deprecated +Encryptor.EncryptionAlgorithm=AES +# For ESAPI Java 2.0 - New encrypt / decrypt methods use this. +Encryptor.CipherTransformation=AES/CBC/PKCS5Padding + +# Applies to ESAPI 2.0 and later only! +# Comma-separated list of cipher modes that provide *BOTH* +# confidentiality *AND* message authenticity. (NIST refers to such cipher +# modes as "combined modes" so that's what we shall call them.) If any of these +# cipher modes are used then no MAC is calculated and stored +# in the CipherText upon encryption. Likewise, if one of these +# cipher modes is used with decryption, no attempt will be made +# to validate the MAC contained in the CipherText object regardless +# of whether it contains one or not. Since the expectation is that +# these cipher modes support support message authenticity already, +# injecting a MAC in the CipherText object would be at best redundant. +# +# Note that as of JDK 1.5, the SunJCE provider does not support *any* +# of these cipher modes. Of these listed, only GCM and CCM are currently +# NIST approved. YMMV for other JCE providers. E.g., Bouncy Castle supports +# GCM and CCM with "NoPadding" mode, but not with "PKCS5Padding" or other +# padding modes. +Encryptor.cipher_modes.combined_modes=GCM,CCM,IAPM,EAX,OCB,CWC + +# Applies to ESAPI 2.0 and later only! +# Additional cipher modes allowed for ESAPI 2.0 encryption. These +# cipher modes are in _addition_ to those specified by the property +# 'Encryptor.cipher_modes.combined_modes'. +# Note: We will add support for streaming modes like CFB & OFB once +# we add support for 'specified' to the property 'Encryptor.ChooseIVMethod' +# (probably in ESAPI 2.1). +# DISCUSS: Better name? +Encryptor.cipher_modes.additional_allowed=CBC + +# 128-bit is almost always sufficient and appears to be more resistant to +# related key attacks than is 256-bit AES. Use '_' to use default key size +# for cipher algorithms (where it makes sense because the algorithm supports +# a variable key size). Key length must agree to what's provided as the +# cipher transformation, otherwise this will be ignored after logging a +# warning. +# +# NOTE: This is what applies BOTH ESAPI 1.4 and 2.0. See warning above about mixing! +Encryptor.EncryptionKeyLength=128 + +# Because 2.0 uses CBC mode by default, it requires an initialization vector (IV). +# (All cipher modes except ECB require an IV.) There are two choices: we can either +# use a fixed IV known to both parties or allow ESAPI to choose a random IV. While +# the IV does not need to be hidden from adversaries, it is important that the +# adversary not be allowed to choose it. Also, random IVs are generally much more +# secure than fixed IVs. (In fact, it is essential that feed-back cipher modes +# such as CFB and OFB use a different IV for each encryption with a given key so +# in such cases, random IVs are much preferred. By default, ESAPI 2.0 uses random +# IVs. If you wish to use 'fixed' IVs, set 'Encryptor.ChooseIVMethod=fixed' and +# uncomment the Encryptor.fixedIV. +# +# Valid values: random|fixed|specified 'specified' not yet implemented; planned for 2.1 +Encryptor.ChooseIVMethod=random +# If you choose to use a fixed IV, then you must place a fixed IV here that +# is known to all others who are sharing your secret key. The format should +# be a hex string that is the same length as the cipher block size for the +# cipher algorithm that you are using. The following is an *example* for AES +# from an AES test vector for AES-128/CBC as described in: +# NIST Special Publication 800-38A (2001 Edition) +# "Recommendation for Block Cipher Modes of Operation". +# (Note that the block size for AES is 16 bytes == 128 bits.) +# +Encryptor.fixedIV=0x000102030405060708090a0b0c0d0e0f + +# Whether or not CipherText should use a message authentication code (MAC) with it. +# This prevents an adversary from altering the IV as well as allowing a more +# fool-proof way of determining the decryption failed because of an incorrect +# key being supplied. This refers to the "separate" MAC calculated and stored +# in CipherText, not part of any MAC that is calculated as a result of a +# "combined mode" cipher mode. +# +# If you are using ESAPI with a FIPS 140-2 cryptographic module, you *must* also +# set this property to false. +Encryptor.CipherText.useMAC=true + +# Whether or not the PlainText object may be overwritten and then marked +# eligible for garbage collection. If not set, this is still treated as 'true'. +Encryptor.PlainText.overwrite=true + +# Do not use DES except in a legacy situations. 56-bit is way too small key size. +#Encryptor.EncryptionKeyLength=56 +#Encryptor.EncryptionAlgorithm=DES + +# TripleDES is considered strong enough for most purposes. +# Note: There is also a 112-bit version of DESede. Using the 168-bit version +# requires downloading the special jurisdiction policy from Sun. +#Encryptor.EncryptionKeyLength=168 +#Encryptor.EncryptionAlgorithm=DESede + +Encryptor.HashAlgorithm=SHA-512 +Encryptor.HashIterations=1024 +Encryptor.DigitalSignatureAlgorithm=SHA1withDSA +Encryptor.DigitalSignatureKeyLength=1024 +Encryptor.RandomAlgorithm=SHA1PRNG +Encryptor.CharacterEncoding=UTF-8 + +# This is the Pseudo Random Function (PRF) that ESAPI's Key Derivation Function +# (KDF) normally uses. Note this is *only* the PRF used for ESAPI's KDF and +# *not* what is used for ESAPI's MAC. (Currently, HmacSHA1 is always used for +# the MAC, mostly to keep the overall size at a minimum.) +# +# Currently supported choices for JDK 1.5 and 1.6 are: +# HmacSHA1 (160 bits), HmacSHA256 (256 bits), HmacSHA384 (384 bits), and +# HmacSHA512 (512 bits). +# Note that HmacMD5 is *not* supported for the PRF used by the KDF even though +# the JDKs support it. See the ESAPI 2.0 Symmetric Encryption User Guide +# further details. +Encryptor.KDF.PRF=HmacSHA256 +#=========================================================================== +# ESAPI HttpUtilties +# +# The HttpUtilities provide basic protections to HTTP requests and responses. Primarily these methods +# protect against malicious data from attackers, such as unprintable characters, escaped characters, +# and other simple attacks. The HttpUtilities also provides utility methods for dealing with cookies, +# headers, and CSRF tokens. +# +# Default file upload location (remember to escape backslashes with \\) +HttpUtilities.UploadDir=C:\\ESAPI\\testUpload +HttpUtilities.UploadTempDir=C:\\temp +# Force flags on cookies, if you use HttpUtilities to set cookies +HttpUtilities.ForceHttpOnlySession=false +HttpUtilities.ForceSecureSession=false +HttpUtilities.ForceHttpOnlyCookies=true +HttpUtilities.ForceSecureCookies=true +# Maximum size of HTTP headers +HttpUtilities.MaxHeaderSize=4096 +# File upload configuration +HttpUtilities.ApprovedUploadExtensions=.zip,.pdf,.doc,.docx,.ppt,.pptx,.tar,.gz,.tgz,.rar,.war,.jar,.ear,.xls,.rtf,.properties,.java,.class,.txt,.xml,.jsp,.jsf,.exe,.dll +HttpUtilities.MaxUploadFileBytes=500000000 +# Using UTF-8 throughout your stack is highly recommended. That includes your database driver, +# container, and any other technologies you may be using. Failure to do this may expose you +# to Unicode transcoding injection attacks. Use of UTF-8 does not hinder internationalization. +HttpUtilities.ResponseContentType=text/html; charset=UTF-8 +# This is the name of the cookie used to represent the HTTP session +# Typically this will be the default "JSESSIONID" +HttpUtilities.HttpSessionIdName=JSESSIONID + + + +#=========================================================================== +# ESAPI Executor +# CHECKME - Not sure what this is used for, but surely it should be made OS independent. +Executor.WorkingDirectory=C:\\Windows\\Temp +Executor.ApprovedExecutables=C:\\Windows\\System32\\cmd.exe,C:\\Windows\\System32\\runas.exe + + +#=========================================================================== +# ESAPI Logging +# Set the application name if these logs are combined with other applications +Logger.ApplicationName=ExampleApplication +# If you use an HTML log viewer that does not properly HTML escape log data, you can set LogEncodingRequired to true +Logger.LogEncodingRequired=false +# Determines whether ESAPI should log the application name. This might be clutter in some single-server/single-app environments. +Logger.LogApplicationName=true +# Determines whether ESAPI should log the server IP and port. This might be clutter in some single-server environments. +Logger.LogServerIP=true +# LogFileName, the name of the logging file. Provide a full directory path (e.g., C:\\ESAPI\\ESAPI_logging_file) if you +# want to place it in a specific directory. +Logger.LogFileName=ESAPI_logging_file +# MaxLogFileSize, the max size (in bytes) of a single log file before it cuts over to a new one (default is 10,000,000) +Logger.MaxLogFileSize=10000000 + + +#=========================================================================== +# ESAPI Intrusion Detection +# +# Each event has a base to which .count, .interval, and .action are added +# The IntrusionException will fire if we receive "count" events within "interval" seconds +# The IntrusionDetector is configurable to take the following actions: log, logout, and disable +# (multiple actions separated by commas are allowed e.g. event.test.actions=log,disable +# +# Custom Events +# Names must start with "event." as the base +# Use IntrusionDetector.addEvent( "test" ) in your code to trigger "event.test" here +# You can also disable intrusion detection completely by changing +# the following parameter to true +# +IntrusionDetector.Disable=false +# +IntrusionDetector.event.test.count=2 +IntrusionDetector.event.test.interval=10 +IntrusionDetector.event.test.actions=disable,log + +# Exception Events +# All EnterpriseSecurityExceptions are registered automatically +# Call IntrusionDetector.getInstance().addException(e) for Exceptions that do not extend EnterpriseSecurityException +# Use the fully qualified classname of the exception as the base + +# any intrusion is an attack +IntrusionDetector.org.owasp.esapi.errors.IntrusionException.count=1 +IntrusionDetector.org.owasp.esapi.errors.IntrusionException.interval=1 +IntrusionDetector.org.owasp.esapi.errors.IntrusionException.actions=log,disable,logout + +# for test purposes +# CHECKME: Shouldn't there be something in the property name itself that designates +# that these are for testing??? +IntrusionDetector.org.owasp.esapi.errors.IntegrityException.count=10 +IntrusionDetector.org.owasp.esapi.errors.IntegrityException.interval=5 +IntrusionDetector.org.owasp.esapi.errors.IntegrityException.actions=log,disable,logout + +# rapid validation errors indicate scans or attacks in progress +# org.owasp.esapi.errors.ValidationException.count=10 +# org.owasp.esapi.errors.ValidationException.interval=10 +# org.owasp.esapi.errors.ValidationException.actions=log,logout + +# sessions jumping between hosts indicates session hijacking +IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.count=2 +IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.interval=10 +IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.actions=log,logout + + +#=========================================================================== +# ESAPI Validation +# +# The ESAPI Validator works on regular expressions with defined names. You can define names +# either here, or you may define application specific patterns in a separate file defined below. +# This allows enterprises to specify both organizational standards as well as application specific +# validation rules. +# +Validator.ConfigurationFile=validation.properties + +# Validators used by ESAPI +Validator.AccountName=^[a-zA-Z0-9]{3,20}$ +Validator.SystemCommand=^[a-zA-Z\\-\\/]{1,64}$ +Validator.RoleName=^[a-z]{1,20}$ + +#the word TEST below should be changed to your application +#name - only relative URL's are supported +Validator.Redirect=^\\/test.*$ + +# Global HTTP Validation Rules +# Values with Base64 encoded data (e.g. encrypted state) will need at least [a-zA-Z0-9\/+=] +Validator.HTTPScheme=^(http|https)$ +Validator.HTTPServerName=^[a-zA-Z0-9_.\\-]*$ +Validator.HTTPParameterName=^[a-zA-Z0-9_]{1,32}$ +Validator.HTTPParameterValue=^[a-zA-Z0-9.\\-\\/+=@_ ]*$ +Validator.HTTPCookieName=^[a-zA-Z0-9\\-_]{1,32}$ +Validator.HTTPCookieValue=^[a-zA-Z0-9\\-\\/+=_ ]*$ +Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,32}$ +Validator.HTTPHeaderValue=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$ +Validator.HTTPContextPath=^\\/?[a-zA-Z0-9.\\-\\/_]*$ +Validator.HTTPServletPath=^[a-zA-Z0-9.\\-\\/_]*$ +Validator.HTTPPath=^[a-zA-Z0-9.\\-_]*$ +Validator.HTTPQueryString=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ %]*$ +Validator.HTTPURI=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$ +Validator.HTTPURL=^.*$ +Validator.HTTPJSESSIONID=^[A-Z0-9]{10,30}$ + +# Validation of file related input +Validator.FileName=^[a-zA-Z0-9!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$ +Validator.DirectoryName=^[a-zA-Z0-9:/\\\\!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$ + +# Validation of dates. Controls whether or not 'lenient' dates are accepted. +# See DataFormat.setLenient(boolean flag) for further details. +Validator.AcceptLenientDates=false + diff --git a/core-java/src/main/resources/data.csv b/core-java/src/main/resources/data.csv new file mode 100644 index 000000000000..ec4ac1044349 --- /dev/null +++ b/core-java/src/main/resources/data.csv @@ -0,0 +1,3 @@ +1|IND|India +2|MY|Malaysia +3|AU|Australia diff --git a/core-java/src/main/resources/logback.xml b/core-java/src/main/resources/logback.xml index 62d0ea5037b2..ec0dc2469ae0 100644 --- a/core-java/src/main/resources/logback.xml +++ b/core-java/src/main/resources/logback.xml @@ -1,5 +1,5 @@ + - web - %date [%thread] %-5level %logger{36} - %message%n @@ -7,10 +7,13 @@ - + + + + + - \ No newline at end of file diff --git a/core-java/src/test/java/com/baeldung/PrimitiveConversionsJUnitTest.java b/core-java/src/test/java/com/baeldung/PrimitiveConversionsJUnitTest.java new file mode 100644 index 000000000000..10ceaf85a41f --- /dev/null +++ b/core-java/src/test/java/com/baeldung/PrimitiveConversionsJUnitTest.java @@ -0,0 +1,125 @@ +package com.baeldung; + +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * + * @author paulo.motta + */ +public class PrimitiveConversionsJUnitTest { + + @Test + public void givenDataWithLessBits_whenAttributingToLargerSizeVariable_thenNoSpecialNotation() { + int myInt = 127; + + long myLong = myInt; + assertEquals(127L, myLong); + + float myFloat = myLong; + assertEquals(127.0f, myFloat, 0.00001f); + + double myDouble = myLong; + assertEquals(127.0, myDouble,0.00001); + } + + @Test + public void givenDataWithMoreBits_whenAttributingToSmallerSizeVariable_thenCastOperatorNeeded() { + + long myLong = 127L; + double myDouble = 127.0; + + float myFloat = (float) myDouble; + assertEquals(127.0f, myFloat, 0.00001f); + + int myInt = (int) myLong; + assertEquals(127, myInt); + + byte myByte = (byte) myInt; + assertEquals( ((byte)127), myByte); + } + + @Test + public void givenPrimitiveData_whenAssiginingToWrapper_thenAutomaticBoxingHappens(){ + int myInt = 127; + + Integer myIntegerReference = myInt; + assertEquals(new Integer("127"), myIntegerReference); + + } + + @Test + public void givenWrapperObjectData_whenAssiginingToPrimitive_thenAutomaticUnboxingHappens(){ + Integer myIntegerReference = new Integer("127"); + + int myOtherInt = myIntegerReference; + assertEquals(127, myOtherInt); + + } + + @Test + public void givenByteValue_whenConvertingToChar_thenWidenAndNarrowTakesPlace(){ + byte myLargeValueByte = (byte) 130; //0b10000010 + System.out.println(myLargeValueByte); //0b10000010 -126 + assertEquals( -126, myLargeValueByte); + + int myLargeValueInt = myLargeValueByte; + System.out.println(myLargeValueInt); //0b11111111 11111111 11111111 10000010 -126 + assertEquals( -126, myLargeValueInt); + + char myLargeValueChar = (char) myLargeValueByte; + System.out.println(myLargeValueChar);//0b11111111 10000010 unsigned 0xFF82 + assertEquals(0xFF82, myLargeValueChar); + + myLargeValueInt = myLargeValueChar; + System.out.println(myLargeValueInt); //0b11111111 10000010 65410 + assertEquals(65410, myLargeValueInt); + + byte myOtherByte = (byte) myLargeValueInt; + System.out.println(myOtherByte); //0b10000010 -126 + assertEquals( -126, myOtherByte); + + + char myLargeValueChar2 = 130; //This is an int not a byte! + System.out.println(myLargeValueChar2);//0b00000000 10000010 unsigned 0x0082 + assertEquals(0x0082, myLargeValueChar2); + + int myLargeValueInt2 = myLargeValueChar2; + System.out.println(myLargeValueInt2); //0b00000000 10000010 130 + assertEquals(130, myLargeValueInt2); + + byte myOtherByte2 = (byte) myLargeValueInt2; + System.out.println(myOtherByte2); //0b10000010 -126 + assertEquals( -126, myOtherByte2); + } + + @Test + public void givenString_whenParsingWithWrappers_thenValuesAreReturned(){ + String myString = "127"; + + byte myNewByte = Byte.parseByte(myString); + assertEquals( ((byte)127), myNewByte); + + short myNewShort = Short.parseShort(myString); + assertEquals( ((short)127), myNewShort); + + int myNewInt = Integer.parseInt(myString); + assertEquals( 127, myNewInt); + + long myNewLong = Long.parseLong(myString); + assertEquals( 127L, myNewLong); + + float myNewFloat = Float.parseFloat(myString); + assertEquals( 127.0f, myNewFloat, 0.00001f); + + double myNewDouble = Double.parseDouble(myString); + assertEquals( 127.0, myNewDouble, 0.00001f); + + boolean myNewBoolean = Boolean.parseBoolean(myString); + assertEquals( false, myNewBoolean); //numbers are not true! + + char myNewChar = myString.charAt(0); + assertEquals( 49, myNewChar); //the value of '1' + } + +} diff --git a/core-java/src/test/java/com/baeldung/arraycopy/ArrayCopyUtilTest.java b/core-java/src/test/java/com/baeldung/arraycopy/ArrayCopyUtilTest.java new file mode 100644 index 000000000000..2235e55338bc --- /dev/null +++ b/core-java/src/test/java/com/baeldung/arraycopy/ArrayCopyUtilTest.java @@ -0,0 +1,168 @@ +package com.baeldung.arraycopy; + +import com.baeldung.arraycopy.model.Address; +import com.baeldung.arraycopy.model.Employee; +import org.apache.commons.lang3.SerializationUtils; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.Arrays; + +public class ArrayCopyUtilTest { + private static Employee[] employees; + private static final int MAX = 2; + + @BeforeClass + public static void setup(){ + createEmployeesArray(); + } + + private static void createEmployeesArray() { + employees = new Employee[MAX]; + Employee employee; + for(int i = 0; i < MAX; i++) { + employee = new Employee(); + employee.setName("Emp"+i); + employee.setId(i); + employees[i] = employee; + } + } + + @Test + public void givenArrayOfPrimitiveType_whenCopiedViaSystemsArrayCopy_thenSuccessful(){ + int[] array = {23, 43, 55}; + int[] copiedArray = new int[3]; + + System.arraycopy(array, 0, copiedArray, 0, 3); + + Assert.assertArrayEquals(copiedArray, array); + } + + @Test + public void givenArrayOfPrimitiveType_whenCopiedSubSequenceViaSystemsArrayCopy_thenSuccessful(){ + int[] array = {23, 43, 55, 12, 65, 88, 92}; + int[] copiedArray = new int[3]; + + System.arraycopy(array, 2, copiedArray, 0, 3); + + Assert.assertTrue(3 == copiedArray.length); + Assert.assertTrue(copiedArray[0] == array[2]); + Assert.assertTrue(copiedArray[1] == array[3]); + Assert.assertTrue(copiedArray[2] == array[4]); + } + + @Test + public void givenArrayOfPrimitiveType_whenCopiedSubSequenceViaArraysCopyOfRange_thenSuccessful(){ + int[] array = {23, 43, 55, 12, 65, 88, 92}; + + int[] copiedArray = Arrays.copyOfRange(array, 1, 4); + + Assert.assertTrue(3 == copiedArray.length); + Assert.assertTrue(copiedArray[0] == array[1]); + Assert.assertTrue(copiedArray[1] == array[2]); + Assert.assertTrue(copiedArray[2] == array[3]); + } + + @Test + public void givenArrayOfPrimitiveType_whenCopiedViaArraysCopyOf_thenValueChangeIsSuccessful(){ + int[] array = {23, 43, 55, 12}; + int newLength = array.length; + + int[] copiedArray = Arrays.copyOf(array, newLength); + + Assert.assertArrayEquals(copiedArray, array); + array[0] = 9; + Assert.assertTrue(copiedArray[0] != array[0]); + copiedArray[1] = 12; + Assert.assertTrue(copiedArray[1] != array[1]); + } + + @Test + public void givenArrayOfNonPrimitiveType_whenCopiedViaArraysCopyOf_thenDoShallowCopy(){ + Employee[] copiedArray = Arrays.copyOf(employees, employees.length); + + Assert.assertArrayEquals(copiedArray, employees); + employees[0].setName(employees[0].getName()+"_Changed"); + //change in employees' element caused change in the copied array + Assert.assertTrue(copiedArray[0].getName().equals(employees[0].getName())); + } + + @Test + public void givenArrayOfPrimitiveType_whenCopiedViaArrayClone_thenValueChangeIsSuccessful(){ + int[] array = {23, 43, 55, 12}; + + int[] copiedArray = array.clone(); + + Assert.assertArrayEquals(copiedArray, array); + array[0] = 9; + Assert.assertTrue(copiedArray[0] != array[0]); + copiedArray[1] = 12; + Assert.assertTrue(copiedArray[1] != array[1]); + } + + @Test + public void givenArraysOfNonPrimitiveType_whenCopiedViaArrayClone_thenDoShallowCopy(){ + Employee[] copiedArray = employees.clone(); + + Assert.assertArrayEquals(copiedArray, employees);; + employees[0].setName(employees[0].getName()+"_Changed"); + //change in employees' element changed the copied array + Assert.assertTrue(copiedArray[0].getName().equals(employees[0].getName())); + } + + @Test + public void givenArraysOfCloneableNonPrimitiveType_whenCopiedViaArrayClone_thenDoShallowCopy(){ + Address[] addresses = createAddressArray(); + + Address[] copiedArray = addresses.clone(); + + addresses[0].setCity(addresses[0].getCity()+"_Changed"); + Assert.assertArrayEquals(copiedArray, addresses); + } + + @Test + public void givenArraysOfSerializableNonPrimitiveType_whenCopiedViaSerializationUtils_thenDoDeepCopy(){ + Employee[] copiedArray = SerializationUtils.clone(employees); + + employees[0].setName(employees[0].getName()+"_Changed"); + //change in employees' element didn't change in the copied array + Assert.assertFalse( + copiedArray[0].getName().equals(employees[0].getName())); + } + + @Test + public void givenArraysOfNonPrimitiveType_whenCopiedViaStream_thenDoShallowCopy(){ + Employee[] copiedArray = Arrays.stream(employees).toArray(Employee[]::new); + + Assert.assertArrayEquals(copiedArray, employees); + employees[0].setName(employees[0].getName()+"_Changed"); + //change in employees' element didn't change in the copied array + Assert.assertTrue(copiedArray[0].getName().equals(employees[0].getName())); + } + + @Test + public void givenArraysOfPrimitiveType_whenCopiedViaStream_thenSuccessful(){ + String[] strArray = {"orange", "red", "green'"}; + + String[] copiedArray = Arrays.stream(strArray).toArray(String[]::new); + + Assert.assertArrayEquals(copiedArray, strArray); + } + + private Address[] createAddressArray(){ + Address[] addresses = new Address[1]; + addresses[0] = createAddress(); + return addresses; + } + + private Address createAddress() { + Address address = new Address(); + address.setCountry("USA"); + address.setState("CA"); + address.setCity("San Francisco"); + address.setStreet("Street 1"); + address.setZipcode("59999"); + return address; + } +} diff --git a/core-java/src/test/java/com/baeldung/concurrent/countdownlatch/CountdownLatchExampleTest.java b/core-java/src/test/java/com/baeldung/concurrent/countdownlatch/CountdownLatchExampleIntegrationTest.java similarity index 98% rename from core-java/src/test/java/com/baeldung/concurrent/countdownlatch/CountdownLatchExampleTest.java rename to core-java/src/test/java/com/baeldung/concurrent/countdownlatch/CountdownLatchExampleIntegrationTest.java index 7bb2d4bb70c2..fc343e4cee93 100644 --- a/core-java/src/test/java/com/baeldung/concurrent/countdownlatch/CountdownLatchExampleTest.java +++ b/core-java/src/test/java/com/baeldung/concurrent/countdownlatch/CountdownLatchExampleIntegrationTest.java @@ -12,7 +12,7 @@ import static java.util.stream.Collectors.toList; import static org.assertj.core.api.Assertions.assertThat; -public class CountdownLatchExampleTest { +public class CountdownLatchExampleIntegrationTest { @Test public void whenParallelProcessing_thenMainThreadWillBlockUntilCompletion() throws InterruptedException { // Given diff --git a/core-java/src/test/java/com/baeldung/enums/PizzaUnitTest.java b/core-java/src/test/java/com/baeldung/enums/PizzaUnitTest.java index 6cf6ad3551e5..35aa07821ce8 100644 --- a/core-java/src/test/java/com/baeldung/enums/PizzaUnitTest.java +++ b/core-java/src/test/java/com/baeldung/enums/PizzaUnitTest.java @@ -1,5 +1,6 @@ package com.baeldung.enums; +import com.baeldung.enums.Pizza.PizzaStatusEnum; import org.junit.Test; import java.util.ArrayList; @@ -69,11 +70,31 @@ public void givenPizaOrders_whenGroupByStatusCalled_thenCorrectlyGrouped() { } @Test - public void givenPizaOrder_whenDelivered_thenPizzaGetsDeliveredAndStatusChanges() { + public void whenDelivered_thenPizzaGetsDeliveredAndStatusChanges() { Pizza pz = new Pizza(); pz.setStatus(Pizza.PizzaStatusEnum.READY); pz.deliver(); assertTrue(pz.getStatus() == Pizza.PizzaStatusEnum.DELIVERED); } + + @Test + public void whenConvertedIntoEnum_thenGetsConvertedCorrectly() { + String pizzaEnumValue = "READY"; + PizzaStatusEnum pizzaStatusEnum = PizzaStatusEnum.valueOf(pizzaEnumValue); + assertTrue(pizzaStatusEnum == PizzaStatusEnum.READY); + } + + @Test(expected = IllegalArgumentException.class) + public void whenConvertedIntoEnum_thenThrowsException() { + String pizzaEnumValue = "rEAdY"; + PizzaStatusEnum pizzaStatusEnum = PizzaStatusEnum.valueOf(pizzaEnumValue); + } + + @Test(expected = IllegalArgumentException.class) + public void givenInvalidEnumValueContentWiseAsString_whenConvertedIntoEnum_thenThrowsException() { + String pizzaEnumValue = "invalid"; + PizzaStatusEnum pizzaStatusEnum = PizzaStatusEnum.valueOf(pizzaEnumValue); + } + } diff --git a/core-java/src/test/java/com/baeldung/java/collections/ConcurrentModificationExceptionTest.java b/core-java/src/test/java/com/baeldung/java/collections/ConcurrentModificationExceptionTest.java new file mode 100644 index 000000000000..100d25ab8d99 --- /dev/null +++ b/core-java/src/test/java/com/baeldung/java/collections/ConcurrentModificationExceptionTest.java @@ -0,0 +1,82 @@ +package com.baeldung.java.collections; + +import org.junit.Test; + +import java.util.*; +import java.util.concurrent.CopyOnWriteArrayList; + +import static java.util.Arrays.asList; +import static org.testng.Assert.assertEquals; + +public class ConcurrentModificationExceptionTest { + @Test + public void changingContentWithSetDoesNotThrowConcurrentModificationException() throws Exception { + ArrayList array = new ArrayList<>(asList(0, "one", 2, "three")); + + for (Object item : array) { + array.set(3, 3); + } + } + + @Test + public void removingElementUsingIteratorAPI() throws Exception { + List originalList = new ArrayList<>(asList("zero", "one", "two", "three")); + + Iterator iterator = originalList.iterator(); + + while (iterator.hasNext()) { + String next = iterator.next(); + if (Objects.equals(next, "one")) iterator.remove(); + } + + assertEquals(originalList, asList("zero", "two", "three")); + } + + @Test + public void modifyingContentAndIteratingUsingListIteratorAPI() throws Exception { + List originalList = new ArrayList<>(asList("zero", "one", "two", "three")); + + ListIterator iterator = originalList.listIterator(); + + while (iterator.hasNext()) { + String next = iterator.next(); + if (Objects.equals(next, "one")) { + iterator.set("another"); + } + + if (Objects.equals(next, "two")) { + iterator.remove(); + } + + if (Objects.equals(next, "three")) { + iterator.add("four"); + } + } + + assertEquals(originalList, asList("zero", "another", "three", "four")); + } + + @Test + public void removingElementUsingCopyAndListAPI() throws Exception { + List originalList = new ArrayList<>(asList("zero", "one", "two", "three")); + + List listCopy = new ArrayList<>(originalList); + + for (String next : listCopy) { + if (Objects.equals(next, "one")) originalList.remove(originalList.indexOf(next) - 1); + } + + assertEquals(originalList, asList("one", "two", "three")); + } + + @Test + public void copyOnWriteList() throws Exception { + List originalList = new CopyOnWriteArrayList<>(asList("zero", "one", "two", "three")); + + for (String next : originalList) { + if (Objects.equals(next, "one")) originalList.remove(originalList.indexOf(next) - 1); + } + + assertEquals(originalList, asList("one", "two", "three")); + } +} diff --git a/core-java/src/test/java/com/baeldung/java8/comparator/Employee.java b/core-java/src/test/java/com/baeldung/java8/comparator/Employee.java new file mode 100644 index 000000000000..bbc4e3e32025 --- /dev/null +++ b/core-java/src/test/java/com/baeldung/java8/comparator/Employee.java @@ -0,0 +1,23 @@ +package com.baeldung.java8.comparator; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Data +@AllArgsConstructor +@ToString +@EqualsAndHashCode +public class Employee implements Comparable{ + String name; + int age; + double salary; + long mobile; + + + @Override + public int compareTo(Employee argEmployee) { + return name.compareTo(argEmployee.getName()); + } +} diff --git a/core-java/src/test/java/com/baeldung/java8/comparator/Java8ComparatorTest.java b/core-java/src/test/java/com/baeldung/java8/comparator/Java8ComparatorTest.java new file mode 100644 index 000000000000..ebcbb7a3fcd1 --- /dev/null +++ b/core-java/src/test/java/com/baeldung/java8/comparator/Java8ComparatorTest.java @@ -0,0 +1,167 @@ +package com.baeldung.java8.comparator; + +import java.util.Arrays; +import java.util.Comparator; + +import org.junit.Before; +import org.junit.Test; + +import lombok.Data; +import static org.junit.Assert.assertTrue; + +public class Java8ComparatorTest { + + private Employee[] employees; + private Employee[] employeesArrayWithNulls; + private Employee[] sortedEmployeesByName; + private Employee[] sortedEmployeesByNameDesc; + private Employee[] sortedEmployeesByAge; + private Employee[] sortedEmployeesByMobile; + private Employee[] sortedEmployeesBySalary; + private Employee[] sortedEmployeesArray_WithNullsFirst; + private Employee[] sortedEmployeesArray_WithNullsLast; + private Employee[] sortedEmployeesByNameAge; + private Employee[] someMoreEmployees; + private Employee[] sortedEmployeesByAgeName;; + + @Before + public void initData() { + employees = new Employee[] { new Employee("John", 25, 3000, 9922001), new Employee("Ace", 22, 2000, 5924001), + new Employee("Keith", 35, 4000, 3924401) }; + employeesArrayWithNulls = new Employee[] { new Employee("John", 25, 3000, 9922001), null, new Employee("Ace", 22, 2000, 5924001), + null, new Employee("Keith", 35, 4000, 3924401) }; + + sortedEmployeesByName = new Employee[] { new Employee("Ace", 22, 2000, 5924001), + new Employee("John", 25, 3000, 9922001), new Employee("Keith", 35, 4000, 3924401) }; + sortedEmployeesByNameDesc = new Employee[] { new Employee("Keith", 35, 4000, 3924401), new Employee("John", 25, 3000, 9922001), + new Employee("Ace", 22, 2000, 5924001) }; + + sortedEmployeesByAge = new Employee[] { new Employee("Ace", 22, 2000, 5924001), + new Employee("John", 25, 3000, 9922001), new Employee("Keith", 35, 4000, 3924401) }; + + sortedEmployeesByMobile = new Employee[] { new Employee("Keith", 35, 4000, 3924401), new Employee("Ace", 22, 2000, 5924001), + new Employee("John", 25, 3000, 9922001), }; + + sortedEmployeesBySalary = new Employee[] { new Employee("Ace", 22, 2000, 5924001), new Employee("John", 25, 3000, 9922001), + new Employee("Keith", 35, 4000, 3924401), }; + + sortedEmployeesArray_WithNullsFirst = new Employee[] { null, null, new Employee("Ace", 22, 2000, 5924001), + new Employee("John", 25, 3000, 9922001), new Employee("Keith", 35, 4000, 3924401) }; + sortedEmployeesArray_WithNullsLast = new Employee[] { new Employee("Ace", 22, 2000, 5924001), + new Employee("John", 25, 3000, 9922001), new Employee("Keith", 35, 4000, 3924401), null, null }; + + someMoreEmployees = new Employee[] { new Employee("Jake", 25, 3000, 9922001), new Employee("Jake", 22, 2000, 5924001), + new Employee("Ace", 22, 3000, 6423001), new Employee("Keith", 35, 4000, 3924401) }; + + sortedEmployeesByAgeName = new Employee[] { new Employee("Ace", 22, 3000, 6423001), + new Employee("Jake", 22, 2000, 5924001), new Employee("Jake", 25, 3000, 9922001), new Employee("Keith", 35, 4000, 3924401) }; + sortedEmployeesByNameAge = new Employee[] { new Employee("Ace", 22, 3000, 6423001), + new Employee("Jake", 22, 2000, 5924001), new Employee("Jake", 25, 3000, 9922001), new Employee("Keith", 35, 4000, 3924401) }; + } + + @Test + public void whenComparing_thenSortedByName() { + Comparator employeeNameComparator = Comparator.comparing(Employee::getName); + Arrays.sort(employees, employeeNameComparator); +// System.out.println(Arrays.toString(employees)); + assertTrue(Arrays.equals(employees, sortedEmployeesByName)); + } + + @Test + public void whenComparingWithComparator_thenSortedByNameDesc() { + Comparator employeeNameComparator = Comparator.comparing(Employee::getName, (s1, s2) -> { + return s2.compareTo(s1); + }); + Arrays.sort(employees, employeeNameComparator); +// System.out.println(Arrays.toString(employees)); + assertTrue(Arrays.equals(employees, sortedEmployeesByNameDesc)); + } + + @Test + public void whenReversed_thenSortedByNameDesc() { + Comparator employeeNameComparator = Comparator.comparing(Employee::getName); + Comparator employeeNameComparatorReversed = employeeNameComparator.reversed(); + Arrays.sort(employees, employeeNameComparatorReversed); +// System.out.println(Arrays.toString(employees)); + assertTrue(Arrays.equals(employees, sortedEmployeesByNameDesc)); + } + + @Test + public void whenComparingInt_thenSortedByAge() { + Comparator employeeAgeComparator = Comparator.comparingInt(Employee::getAge); + Arrays.sort(employees, employeeAgeComparator); +// System.out.println(Arrays.toString(employees)); + assertTrue(Arrays.equals(employees, sortedEmployeesByAge)); + } + + @Test + public void whenComparingLong_thenSortedByMobile() { + Comparator employeeMobileComparator = Comparator.comparingLong(Employee::getMobile); + Arrays.sort(employees, employeeMobileComparator); +// System.out.println(Arrays.toString(employees)); + assertTrue(Arrays.equals(employees, sortedEmployeesByMobile)); + } + + @Test + public void whenComparingDouble_thenSortedBySalary() { + Comparator employeeSalaryComparator = Comparator.comparingDouble(Employee::getSalary); + Arrays.sort(employees, employeeSalaryComparator); +// System.out.println(Arrays.toString(employees)); + assertTrue(Arrays.equals(employees, sortedEmployeesBySalary)); + } + + @Test + public void whenNaturalOrder_thenSortedByName() { + Comparator employeeNameComparator = Comparator. naturalOrder(); + Arrays.sort(employees, employeeNameComparator); +// System.out.println(Arrays.toString(employees)); + assertTrue(Arrays.equals(employees, sortedEmployeesByName)); + } + + @Test + public void whenReverseOrder_thenSortedByNameDesc() { + Comparator employeeNameComparator = Comparator. reverseOrder(); + Arrays.sort(employees, employeeNameComparator); +// System.out.println(Arrays.toString(employees)); + assertTrue(Arrays.equals(employees, sortedEmployeesByNameDesc)); + } + + @Test + public void whenNullsFirst_thenSortedByNameWithNullsFirst() { + Comparator employeeNameComparator = Comparator.comparing(Employee::getName); + Comparator employeeNameComparator_nullFirst = Comparator.nullsFirst(employeeNameComparator); + Arrays.sort(employeesArrayWithNulls, employeeNameComparator_nullFirst); +// System.out.println(Arrays.toString(employeesArrayWithNulls)); + assertTrue(Arrays.equals(employeesArrayWithNulls, sortedEmployeesArray_WithNullsFirst)); + } + + @Test + public void whenNullsLast_thenSortedByNameWithNullsLast() { + Comparator employeeNameComparator = Comparator.comparing(Employee::getName); + Comparator employeeNameComparator_nullLast = Comparator.nullsLast(employeeNameComparator); + Arrays.sort(employeesArrayWithNulls, employeeNameComparator_nullLast); +// System.out.println(Arrays.toString(employeesArrayWithNulls)); + assertTrue(Arrays.equals(employeesArrayWithNulls, sortedEmployeesArray_WithNullsLast)); + } + + @Test + public void whenThenComparing_thenSortedByAgeName() { + Comparator employee_Age_Name_Comparator = Comparator.comparing(Employee::getAge).thenComparing(Employee::getName); + + Arrays.sort(someMoreEmployees, employee_Age_Name_Comparator); +// System.out.println(Arrays.toString(someMoreEmployees)); + assertTrue(Arrays.equals(someMoreEmployees, sortedEmployeesByAgeName)); + } + + @Test + public void whenThenComparing_thenSortedByNameAge() { + Comparator employee_Name_Age_Comparator = Comparator.comparing(Employee::getName).thenComparingInt(Employee::getAge); + + Arrays.sort(someMoreEmployees, employee_Name_Age_Comparator); +// System.out.println(Arrays.toString(someMoreEmployees)); + assertTrue(Arrays.equals(someMoreEmployees, sortedEmployeesByNameAge)); + } + + +} + diff --git a/core-java/src/test/java/com/baeldung/junit4vstestng/SortedTests.java b/core-java/src/test/java/com/baeldung/junit4vstestng/SortedTests.java new file mode 100644 index 000000000000..fe0ec1469c7d --- /dev/null +++ b/core-java/src/test/java/com/baeldung/junit4vstestng/SortedTests.java @@ -0,0 +1,22 @@ +package com.baeldung.junit4vstestng; + +import static org.junit.Assert.assertTrue; + +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class SortedTests { + + @Test + public void a_givenString_whenChangedtoInt_thenTrue() { + assertTrue(Integer.valueOf("10") instanceof Integer); + } + + @Test + public void b_givenInt_whenChangedtoString_thenTrue() { + assertTrue(String.valueOf(10) instanceof String); + } + +} diff --git a/core-java/src/test/java/com/baeldung/list/flattennestedlist/FlattenNestedListTest.java b/core-java/src/test/java/com/baeldung/list/flattennestedlist/FlattenNestedListTest.java new file mode 100644 index 000000000000..93962e7831ba --- /dev/null +++ b/core-java/src/test/java/com/baeldung/list/flattennestedlist/FlattenNestedListTest.java @@ -0,0 +1,48 @@ +package com.baeldung.list.flattennestedlist; + +import static java.util.Arrays.asList; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +import org.hamcrest.collection.IsIterableContainingInOrder; +import org.junit.Test; + +public class FlattenNestedListTest { + List> lol = asList(asList("one:one"), asList("two:one", "two:two", "two:three"), asList("three:one", "three:two", "three:three", "three:four")); + + @Test + public void givenNestedList_thenFlattenImperatively() { + List ls = flattenListOfListsImperatively(lol); + + assertNotNull(ls); + assertTrue(ls.size() == 8); + // assert content + assertThat(ls, IsIterableContainingInOrder.contains("one:one", "two:one", "two:two", "two:three", "three:one", "three:two", "three:three", "three:four")); + } + + @Test + public void givenNestedList_thenFlattenFunctionally() { + List ls = flattenListOfListsStream(lol); + + assertNotNull(ls); + assertTrue(ls.size() == 8); + // assert content + assertThat(ls, IsIterableContainingInOrder.contains("one:one", "two:one", "two:two", "two:three", "three:one", "three:two", "three:three", "three:four")); + } + + public List flattenListOfListsImperatively(List> list) { + List ls = new ArrayList<>(); + list.forEach(ls::addAll); + return ls; + } + + public List flattenListOfListsStream(List> list) { + return list.stream().flatMap(Collection::stream).collect(Collectors.toList()); + } +} diff --git a/core-java/src/test/java/com/baeldung/list/listoflist/ListOfListsTest.java b/core-java/src/test/java/com/baeldung/list/listoflist/ListOfListsTest.java index ce24ff24bc57..674a2f89bc97 100644 --- a/core-java/src/test/java/com/baeldung/list/listoflist/ListOfListsTest.java +++ b/core-java/src/test/java/com/baeldung/list/listoflist/ListOfListsTest.java @@ -39,7 +39,8 @@ public void givenListOfLists_thenCheckNames() { @SuppressWarnings("unchecked") @Test public void givenListOfLists_whenRemovingElements_thenCheckNames() { - ((ArrayList) listOfLists.get(1)).remove(0); + + ((ArrayList) listOfLists.get(1)).remove(0); listOfLists.remove(1); assertEquals("Rubber 1", ((Rubber) listOfLists.get(1) .get(0)).getName()); @@ -47,4 +48,29 @@ public void givenListOfLists_whenRemovingElements_thenCheckNames() { assertEquals("Rubber 1", ((Rubber) listOfLists.get(0) .get(0)).getName()); } + + @Test + public void givenThreeList_whenCombineIntoOneList_thenCheckList() { + ArrayList pens = new ArrayList<>(); + pens.add(new Pen("Pen 1")); + pens.add(new Pen("Pen 2")); + ArrayList pencils = new ArrayList<>(); + pencils.add(new Pencil("Pencil 1")); + pencils.add(new Pencil("Pencil 2")); + ArrayList rubbers = new ArrayList<>(); + rubbers.add(new Rubber("Rubber 1")); + rubbers.add(new Rubber("Rubber 2")); + + List> list = new ArrayList>(); + list.add(pens); + list.add(pencils); + list.add(rubbers); + + assertEquals("Pen 1", ((Pen) list.get(0) + .get(0)).getName()); + assertEquals("Pencil 1", ((Pencil) list.get(1) + .get(0)).getName()); + assertEquals("Rubber 1", ((Rubber) list.get(2) + .get(0)).getName()); + } } diff --git a/core-java/src/test/java/com/baeldung/money/JavaMoneyTest.java b/core-java/src/test/java/com/baeldung/money/JavaMoneyTest.java new file mode 100644 index 000000000000..140560d07948 --- /dev/null +++ b/core-java/src/test/java/com/baeldung/money/JavaMoneyTest.java @@ -0,0 +1,186 @@ +package com.baeldung.money; + +import org.javamoney.moneta.FastMoney; +import org.javamoney.moneta.Money; +import org.javamoney.moneta.format.CurrencyStyle; +import org.junit.Test; + +import javax.money.CurrencyUnit; +import javax.money.Monetary; +import javax.money.MonetaryAmount; +import javax.money.UnknownCurrencyException; +import javax.money.convert.ConversionQueryBuilder; +import javax.money.convert.CurrencyConversion; +import javax.money.convert.MonetaryConversions; +import javax.money.format.AmountFormatQueryBuilder; +import javax.money.format.MonetaryAmountFormat; +import javax.money.format.MonetaryFormats; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +import static org.junit.Assert.*; + +public class JavaMoneyTest { + + @Test + public void givenCurrencyCode_whenString_thanExist() { + CurrencyUnit usd = Monetary.getCurrency("USD"); + + assertNotNull(usd); + assertEquals(usd.getCurrencyCode(), "USD"); + assertEquals(usd.getNumericCode(), 840); + assertEquals(usd.getDefaultFractionDigits(), 2); + } + + @Test(expected = UnknownCurrencyException.class) + public void givenCurrencyCode_whenNoExist_thanThrowsError() { + Monetary.getCurrency("AAA"); + fail(); // if no exception + } + + @Test + public void givenAmounts_whenStringified_thanEquals() { + CurrencyUnit usd = Monetary.getCurrency("USD"); + MonetaryAmount fstAmtUSD = Monetary + .getDefaultAmountFactory() + .setCurrency(usd) + .setNumber(200) + .create(); + Money moneyof = Money.of(12, usd); + FastMoney fastmoneyof = FastMoney.of(2, usd); + + assertEquals("USD", usd.toString()); + assertEquals("USD 200", fstAmtUSD.toString()); + assertEquals("USD 12", moneyof.toString()); + assertEquals("USD 2.00000", fastmoneyof.toString()); + } + + @Test + public void givenCurrencies_whenCompared_thanNotequal() { + MonetaryAmount oneDolar = Monetary + .getDefaultAmountFactory() + .setCurrency("USD") + .setNumber(1) + .create(); + Money oneEuro = Money.of(1, "EUR"); + + assertFalse(oneEuro.equals(FastMoney.of(1, "EUR"))); + assertTrue(oneDolar.equals(Money.of(1, "USD"))); + } + + @Test(expected = ArithmeticException.class) + public void givenAmount_whenDivided_thanThrowsException() { + MonetaryAmount oneDolar = Monetary + .getDefaultAmountFactory() + .setCurrency("USD") + .setNumber(1) + .create(); + oneDolar.divide(3); + fail(); // if no exception + } + + @Test + public void givenAmounts_whenSummed_thanCorrect() { + List monetaryAmounts = Arrays.asList(Money.of(100, "CHF"), Money.of(10.20, "CHF"), Money.of(1.15, "CHF")); + + Money sumAmtCHF = (Money) monetaryAmounts + .stream() + .reduce(Money.of(0, "CHF"), MonetaryAmount::add); + + assertEquals("CHF 111.35", sumAmtCHF.toString()); + } + + @Test + public void givenArithmetic_whenStringified_thanEqualsAmount() { + CurrencyUnit usd = Monetary.getCurrency("USD"); + + Money moneyof = Money.of(12, usd); + MonetaryAmount fstAmtUSD = Monetary + .getDefaultAmountFactory() + .setCurrency(usd) + .setNumber(200.50) + .create(); + MonetaryAmount oneDolar = Monetary + .getDefaultAmountFactory() + .setCurrency("USD") + .setNumber(1) + .create(); + Money subtractedAmount = Money + .of(1, "USD") + .subtract(fstAmtUSD); + MonetaryAmount multiplyAmount = oneDolar.multiply(0.25); + MonetaryAmount divideAmount = oneDolar.divide(0.25); + + assertEquals("USD", usd.toString()); + assertEquals("USD 1", oneDolar.toString()); + assertEquals("USD 200.5", fstAmtUSD.toString()); + assertEquals("USD 12", moneyof.toString()); + assertEquals("USD -199.5", subtractedAmount.toString()); + assertEquals("USD 0.25", multiplyAmount.toString()); + assertEquals("USD 4", divideAmount.toString()); + } + + @Test + public void givenAmount_whenRounded_thanEquals() { + MonetaryAmount fstAmtEUR = Monetary + .getDefaultAmountFactory() + .setCurrency("EUR") + .setNumber(1.30473908) + .create(); + MonetaryAmount roundEUR = fstAmtEUR.with(Monetary.getDefaultRounding()); + assertEquals("EUR 1.30473908", fstAmtEUR.toString()); + assertEquals("EUR 1.3", roundEUR.toString()); + } + + @Test + public void givenAmount_whenConversion_thenNotNull() { + MonetaryAmount oneDollar = Monetary + .getDefaultAmountFactory() + .setCurrency("USD") + .setNumber(1) + .create(); + + CurrencyConversion conversionEUR = MonetaryConversions.getConversion("EUR"); + + MonetaryAmount convertedAmountUSDtoEUR = oneDollar.with(conversionEUR); + + assertEquals("USD 1", oneDollar.toString()); + assertNotNull(convertedAmountUSDtoEUR); + } + + @Test + public void givenLocale_whenFormatted_thanEquals() { + MonetaryAmount oneDollar = Monetary + .getDefaultAmountFactory() + .setCurrency("USD") + .setNumber(1) + .create(); + MonetaryAmountFormat formatUSD = MonetaryFormats.getAmountFormat(Locale.US); + String usFormatted = formatUSD.format(oneDollar); + + assertEquals("USD 1", oneDollar.toString()); + assertNotNull(formatUSD); + assertEquals("USD1.00", usFormatted); + } + + @Test + public void givenAmount_whenCustomFormat_thanEquals() { + MonetaryAmount oneDollar = Monetary + .getDefaultAmountFactory() + .setCurrency("USD") + .setNumber(1) + .create(); + + MonetaryAmountFormat customFormat = MonetaryFormats.getAmountFormat(AmountFormatQueryBuilder + .of(Locale.US) + .set(CurrencyStyle.NAME) + .set("pattern", "00000.00 ¤") + .build()); + String customFormatted = customFormat.format(oneDollar); + + assertNotNull(customFormat); + assertEquals("USD 1", oneDollar.toString()); + assertEquals("00001.00 US Dollar", customFormatted); + } +} diff --git a/core-java/src/test/java/com/baeldung/stringtokenizer/TokenizerTest.java b/core-java/src/test/java/com/baeldung/stringtokenizer/TokenizerTest.java new file mode 100644 index 000000000000..eed42a2f9678 --- /dev/null +++ b/core-java/src/test/java/com/baeldung/stringtokenizer/TokenizerTest.java @@ -0,0 +1,29 @@ +package com.baeldung.stringtokenizer; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +public class TokenizerTest { + + private final MyTokenizer myTokenizer = new MyTokenizer(); + private final List expectedTokensForString = Arrays.asList("Welcome", "to", "baeldung.com"); + private final List expectedTokensForFile = Arrays.asList("1", "IND", "India", "2", "MY", "Malaysia", "3", "AU", "Australia"); + + @Test + public void givenString_thenGetListOfString() { + String str = "Welcome,to,baeldung.com"; + List actualTokens = myTokenizer.getTokens(str); + assertEquals(expectedTokensForString, actualTokens); + } + + @Test + public void givenFile_thenGetListOfString() { + List actualTokens = myTokenizer.getTokensFromFile("data.csv", "|"); + assertEquals(expectedTokensForFile, actualTokens); + } + +} diff --git a/core-java/src/test/java/com/baeldung/synchronousqueue/SynchronousQueueTest.java b/core-java/src/test/java/com/baeldung/synchronousqueue/SynchronousQueueTest.java new file mode 100644 index 000000000000..5d73e02e4f08 --- /dev/null +++ b/core-java/src/test/java/com/baeldung/synchronousqueue/SynchronousQueueTest.java @@ -0,0 +1,83 @@ +package com.baeldung.synchronousqueue; + +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; + +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; + +import static junit.framework.TestCase.assertEquals; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class SynchronousQueueTest { + + @Test + public void givenTwoThreads_whenWantToExchangeUsingLockGuardedVariable_thenItSucceed() throws InterruptedException { + //given + ExecutorService executor = Executors.newFixedThreadPool(2); + AtomicInteger sharedState = new AtomicInteger(); + CountDownLatch countDownLatch = new CountDownLatch(1); + + Runnable producer = () -> { + Integer producedElement = ThreadLocalRandom.current().nextInt(); + System.out.println("Saving an element: " + producedElement + " to the exchange point"); + sharedState.set(producedElement); + countDownLatch.countDown(); + }; + + Runnable consumer = () -> { + try { + countDownLatch.await(); + Integer consumedElement = sharedState.get(); + System.out.println("consumed an element: " + consumedElement + " from the exchange point"); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + }; + + //when + executor.execute(producer); + executor.execute(consumer); + + //then + executor.awaitTermination(500, TimeUnit.MILLISECONDS); + executor.shutdown(); + assertEquals(countDownLatch.getCount(), 0); + } + + @Test + public void givenTwoThreads_whenWantToExchangeUsingSynchronousQueue_thenItSucceed() throws InterruptedException { + //given + ExecutorService executor = Executors.newFixedThreadPool(2); + final SynchronousQueue queue = new SynchronousQueue<>(); + + Runnable producer = () -> { + Integer producedElement = ThreadLocalRandom.current().nextInt(); + try { + System.out.println("Saving an element: " + producedElement + " to the exchange point"); + queue.put(producedElement); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + }; + + Runnable consumer = () -> { + try { + Integer consumedElement = queue.take(); + System.out.println("consumed an element: " + consumedElement + " from the exchange point"); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + }; + + //when + executor.execute(producer); + executor.execute(consumer); + + //then + executor.awaitTermination(500, TimeUnit.MILLISECONDS); + executor.shutdown(); + assertEquals(queue.size(), 0); + } +} \ No newline at end of file diff --git a/core-java/src/test/java/com/baeldung/threadlocal/ThreadLocalTest.java b/core-java/src/test/java/com/baeldung/threadlocal/ThreadLocalTest.java new file mode 100644 index 000000000000..ac2e8fbe63c3 --- /dev/null +++ b/core-java/src/test/java/com/baeldung/threadlocal/ThreadLocalTest.java @@ -0,0 +1,35 @@ +package com.baeldung.threadlocal; + + +import org.junit.Test; + +import java.util.concurrent.ExecutionException; + +import static org.junit.Assert.assertEquals; + +public class ThreadLocalTest { + @Test + public void givenThreadThatStoresContextInAMap_whenStartThread_thenShouldSetContextForBothUsers() throws ExecutionException, InterruptedException { + //when + SharedMapWithUserContext firstUser = new SharedMapWithUserContext(1); + SharedMapWithUserContext secondUser = new SharedMapWithUserContext(2); + new Thread(firstUser).start(); + new Thread(secondUser).start(); + + Thread.sleep(3000); + //then + assertEquals(SharedMapWithUserContext.userContextPerUserId.size(), 2); + } + + @Test + public void givenThreadThatStoresContextInThreadLocal_whenStartThread_thenShouldStoreContextInThreadLocal() throws ExecutionException, InterruptedException { + //when + ThreadLocalWithUserContext firstUser = new ThreadLocalWithUserContext(1); + ThreadLocalWithUserContext secondUser = new ThreadLocalWithUserContext(2); + new Thread(firstUser).start(); + new Thread(secondUser).start(); + + Thread.sleep(3000); + } + +} diff --git a/core-java/src/test/java/com/baeldung/transferqueue/TransferQueueTest.java b/core-java/src/test/java/com/baeldung/transferqueue/TransferQueueTest.java new file mode 100644 index 000000000000..4a1a663ce413 --- /dev/null +++ b/core-java/src/test/java/com/baeldung/transferqueue/TransferQueueTest.java @@ -0,0 +1,74 @@ +package com.baeldung.transferqueue; + +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; + +import java.util.concurrent.*; + +import static junit.framework.TestCase.assertEquals; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class TransferQueueTest { + + @Test + public void givenTransferQueue_whenUseMultipleConsumersAndMultipleProducers_thenShouldProcessAllMessages() throws InterruptedException { + //given + TransferQueue transferQueue = new LinkedTransferQueue<>(); + ExecutorService exService = Executors.newFixedThreadPool(3); + Producer producer1 = new Producer(transferQueue, "1", 3); + Producer producer2 = new Producer(transferQueue, "2", 3); + Consumer consumer1 = new Consumer(transferQueue, "1", 3); + Consumer consumer2 = new Consumer(transferQueue, "2", 3); + + //when + exService.execute(producer1); + exService.execute(producer2); + exService.execute(consumer1); + exService.execute(consumer2); + + //then + exService.awaitTermination(10_000, TimeUnit.MILLISECONDS); + exService.shutdown(); + + assertEquals(producer1.numberOfProducedMessages.intValue(), 3); + assertEquals(producer2.numberOfProducedMessages.intValue(), 3); + } + + @Test + public void givenTransferQueue_whenUseOneConsumerAndOneProducer_thenShouldProcessAllMessages() throws InterruptedException { + //given + TransferQueue transferQueue = new LinkedTransferQueue<>(); + ExecutorService exService = Executors.newFixedThreadPool(2); + Producer producer = new Producer(transferQueue, "1", 3); + Consumer consumer = new Consumer(transferQueue, "1", 3); + + //when + exService.execute(producer); + exService.execute(consumer); + + //then + exService.awaitTermination(5000, TimeUnit.MILLISECONDS); + exService.shutdown(); + + assertEquals(producer.numberOfProducedMessages.intValue(), 3); + assertEquals(consumer.numberOfConsumedMessages.intValue(), 3); + } + + @Test + public void givenTransferQueue_whenUseOneProducerAndNoConsumers_thenShouldFailWithTimeout() throws InterruptedException { + //given + TransferQueue transferQueue = new LinkedTransferQueue<>(); + ExecutorService exService = Executors.newFixedThreadPool(2); + Producer producer = new Producer(transferQueue, "1", 3); + + //when + exService.execute(producer); + + //then + exService.awaitTermination(5000, TimeUnit.MILLISECONDS); + exService.shutdown(); + + assertEquals(producer.numberOfProducedMessages.intValue(), 0); + } +} diff --git a/core-java/src/test/java/com/baeldung/unsafe/CASCounter.java b/core-java/src/test/java/com/baeldung/unsafe/CASCounter.java new file mode 100644 index 000000000000..f7f3b340c250 --- /dev/null +++ b/core-java/src/test/java/com/baeldung/unsafe/CASCounter.java @@ -0,0 +1,33 @@ +package com.baeldung.unsafe; + +import sun.misc.Unsafe; + +import java.lang.reflect.Field; + +class CASCounter { + private final Unsafe unsafe; + private volatile long counter = 0; + private long offset; + + private Unsafe getUnsafe() throws IllegalAccessException, NoSuchFieldException { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return (Unsafe) f.get(null); + } + + public CASCounter() throws Exception { + unsafe = getUnsafe(); + offset = unsafe.objectFieldOffset(CASCounter.class.getDeclaredField("counter")); + } + + public void increment() { + long before = counter; + while (!unsafe.compareAndSwapLong(this, offset, before, before + 1)) { + before = counter; + } + } + + public long getCounter() { + return counter; + } +} \ No newline at end of file diff --git a/core-java/src/test/java/com/baeldung/unsafe/OffHeapArray.java b/core-java/src/test/java/com/baeldung/unsafe/OffHeapArray.java new file mode 100644 index 000000000000..f5cab88f3d2e --- /dev/null +++ b/core-java/src/test/java/com/baeldung/unsafe/OffHeapArray.java @@ -0,0 +1,39 @@ +package com.baeldung.unsafe; + +import sun.misc.Unsafe; + +import java.lang.reflect.Field; + +class OffHeapArray { + private final static int BYTE = 1; + private long size; + private long address; + + private Unsafe getUnsafe() throws IllegalAccessException, NoSuchFieldException { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return (Unsafe) f.get(null); + } + + public OffHeapArray(long size) throws NoSuchFieldException, IllegalAccessException { + this.size = size; + address = getUnsafe().allocateMemory(size * BYTE); + } + + public void set(long i, byte value) throws NoSuchFieldException, IllegalAccessException { + getUnsafe().putByte(address + i * BYTE, value); + } + + public int get(long idx) throws NoSuchFieldException, IllegalAccessException { + return getUnsafe().getByte(address + idx * BYTE); + } + + public long size() { + return size; + } + + public void freeMemory() throws NoSuchFieldException, IllegalAccessException { + getUnsafe().freeMemory(address); + } + +} diff --git a/core-java/src/test/java/com/baeldung/unsafe/UnsafeTest.java b/core-java/src/test/java/com/baeldung/unsafe/UnsafeTest.java new file mode 100644 index 000000000000..9f53e38969d4 --- /dev/null +++ b/core-java/src/test/java/com/baeldung/unsafe/UnsafeTest.java @@ -0,0 +1,123 @@ +package com.baeldung.unsafe; + +import org.junit.Before; +import org.junit.Test; +import sun.misc.Unsafe; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.stream.IntStream; + +import static junit.framework.Assert.assertTrue; +import static junit.framework.TestCase.assertEquals; + +public class UnsafeTest { + + private Unsafe unsafe; + + @Before + public void setup() throws NoSuchFieldException, IllegalAccessException { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + unsafe = (Unsafe) f.get(null); + } + + + @Test + public void givenClass_whenInitializeIt_thenShouldHaveDifferentStateWhenUseUnsafe() throws IllegalAccessException, InstantiationException { + //when + InitializationOrdering o1 = new InitializationOrdering(); + assertEquals(o1.getA(), 1); + + //when + InitializationOrdering o3 = (InitializationOrdering) unsafe.allocateInstance(InitializationOrdering.class); + assertEquals(o3.getA(), 0); + } + + @Test + public void givenPrivateMethod_whenUsingUnsafe_thenCanModifyPrivateField() throws NoSuchFieldException { + //given + SecretHolder secretHolder = new SecretHolder(); + + //when + Field f = secretHolder.getClass().getDeclaredField("SECRET_VALUE"); + unsafe.putInt(secretHolder, unsafe.objectFieldOffset(f), 1); + + //then + assertTrue(secretHolder.secretIsDisclosed()); + } + + @Test(expected = IOException.class) + public void givenUnsafeThrowException_whenThrowCheckedException_thenNotNeedToCatchIt() { + unsafe.throwException(new IOException()); + } + + @Test + public void givenArrayBiggerThatMaxInt_whenAllocateItOffHeapMemory_thenSuccess() throws NoSuchFieldException, IllegalAccessException { + //given + long SUPER_SIZE = (long) Integer.MAX_VALUE * 2; + OffHeapArray array = new OffHeapArray(SUPER_SIZE); + + //when + int sum = 0; + for (int i = 0; i < 100; i++) { + array.set((long) Integer.MAX_VALUE + i, (byte) 3); + sum += array.get((long) Integer.MAX_VALUE + i); + } + + long arraySize = array.size(); + + array.freeMemory(); + + //then + assertEquals(arraySize, SUPER_SIZE); + assertEquals(sum, 300); + + } + + @Test + public void givenUnsafeCompareAndSwap_whenUseIt_thenCounterYildCorrectLockFreeResults() throws Exception { + //given + int NUM_OF_THREADS = 1_000; + int NUM_OF_INCREMENTS = 10_000; + ExecutorService service = Executors.newFixedThreadPool(NUM_OF_THREADS); + CASCounter casCounter = new CASCounter(); + + //when + IntStream.rangeClosed(0, NUM_OF_THREADS - 1) + .forEach(i -> service.submit(() -> IntStream + .rangeClosed(0, NUM_OF_INCREMENTS - 1) + .forEach(j -> casCounter.increment()))); + + service.shutdown(); + service.awaitTermination(1, TimeUnit.MINUTES); + + //then + assertEquals(NUM_OF_INCREMENTS * NUM_OF_THREADS, casCounter.getCounter()); + + } + + class InitializationOrdering { + private long a; + + public InitializationOrdering() { + this.a = 1; + } + + public long getA() { + return this.a; + } + } + + class SecretHolder { + private int SECRET_VALUE = 0; + + public boolean secretIsDisclosed() { + return SECRET_VALUE == 1; + } + } + +} diff --git a/core-java/src/test/java/org/baeldung/core/exceptions/FileNotFoundExceptionUnitTest.java b/core-java/src/test/java/org/baeldung/core/exceptions/FileNotFoundExceptionUnitTest.java index e615e6a7d1df..e8007ad4c4de 100644 --- a/core-java/src/test/java/org/baeldung/core/exceptions/FileNotFoundExceptionUnitTest.java +++ b/core-java/src/test/java/org/baeldung/core/exceptions/FileNotFoundExceptionUnitTest.java @@ -6,12 +6,14 @@ import java.io.FileReader; import java.io.IOException; -import org.apache.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import org.junit.Test; public class FileNotFoundExceptionUnitTest { - private static final Logger LOG = Logger.getLogger(FileNotFoundExceptionUnitTest.class); + private static final Logger LOG = LoggerFactory.getLogger(FileNotFoundExceptionUnitTest.class); private String fileName = Double.toString(Math.random()); diff --git a/core-java/src/test/java/org/baeldung/java/shell/JavaProcessUnitTest.java b/core-java/src/test/java/org/baeldung/java/shell/JavaProcessUnitIntegrationTest.java similarity index 98% rename from core-java/src/test/java/org/baeldung/java/shell/JavaProcessUnitTest.java rename to core-java/src/test/java/org/baeldung/java/shell/JavaProcessUnitIntegrationTest.java index 2c330c513d19..2e38886271bb 100644 --- a/core-java/src/test/java/org/baeldung/java/shell/JavaProcessUnitTest.java +++ b/core-java/src/test/java/org/baeldung/java/shell/JavaProcessUnitIntegrationTest.java @@ -7,7 +7,7 @@ import java.util.concurrent.Executors; import java.util.function.Consumer; -public class JavaProcessUnitTest { +public class JavaProcessUnitIntegrationTest { private static final boolean IS_WINDOWS = System.getProperty("os.name").toLowerCase().startsWith("windows"); private static class StreamGobbler implements Runnable { diff --git a/core-java/src/test/java/org/baeldung/java/streams/ThreadPoolInParallelStreamTest.java b/core-java/src/test/java/org/baeldung/java/streams/ThreadPoolInParallelStreamIntegrationTest.java similarity index 93% rename from core-java/src/test/java/org/baeldung/java/streams/ThreadPoolInParallelStreamTest.java rename to core-java/src/test/java/org/baeldung/java/streams/ThreadPoolInParallelStreamIntegrationTest.java index c2eb1cff5d5f..42e85fc586b1 100644 --- a/core-java/src/test/java/org/baeldung/java/streams/ThreadPoolInParallelStreamTest.java +++ b/core-java/src/test/java/org/baeldung/java/streams/ThreadPoolInParallelStreamIntegrationTest.java @@ -1,38 +1,38 @@ -package org.baeldung.java.streams; - -import org.junit.Test; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ForkJoinPool; -import java.util.stream.Collectors; -import java.util.stream.LongStream; -import java.util.stream.Stream; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -public class ThreadPoolInParallelStreamTest { - - @Test - public void giveRangeOfLongs_whenSummedInParallel_shouldBeEqualToExpectedTotal() throws InterruptedException, ExecutionException { - long firstNum = 1; - long lastNum = 1_000_000; - - List aList = LongStream.rangeClosed(firstNum, lastNum).boxed().collect(Collectors.toList()); - - ForkJoinPool customThreadPool = new ForkJoinPool(4); - long actualTotal = customThreadPool.submit(() -> aList.parallelStream().reduce(0L, Long::sum)).get(); - - assertEquals((lastNum + firstNum) * lastNum / 2, actualTotal); - } - - @Test - public void givenList_whenCallingParallelStream_shouldBeParallelStream() { - List aList = new ArrayList<>(); - Stream parallelStream = aList.parallelStream(); - - assertTrue(parallelStream.isParallel()); - } -} +package org.baeldung.java.streams; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ForkJoinPool; +import java.util.stream.Collectors; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class ThreadPoolInParallelStreamIntegrationTest { + + @Test + public void giveRangeOfLongs_whenSummedInParallel_shouldBeEqualToExpectedTotal() throws InterruptedException, ExecutionException { + long firstNum = 1; + long lastNum = 1_000_000; + + List aList = LongStream.rangeClosed(firstNum, lastNum).boxed().collect(Collectors.toList()); + + ForkJoinPool customThreadPool = new ForkJoinPool(4); + long actualTotal = customThreadPool.submit(() -> aList.parallelStream().reduce(0L, Long::sum)).get(); + + assertEquals((lastNum + firstNum) * lastNum / 2, actualTotal); + } + + @Test + public void givenList_whenCallingParallelStream_shouldBeParallelStream() { + List aList = new ArrayList<>(); + Stream parallelStream = aList.parallelStream(); + + assertTrue(parallelStream.isParallel()); + } +} diff --git a/couchbase-sdk/pom.xml b/couchbase-sdk/pom.xml index 1200fab4543f..d592a15745f5 100644 --- a/couchbase-sdk/pom.xml +++ b/couchbase-sdk/pom.xml @@ -22,11 +22,23 @@ org.springframework spring-context ${spring-framework.version} + + + commons-logging + commons-logging + + org.springframework spring-context-support ${spring-framework.version} + + + commons-logging + commons-logging + + @@ -46,11 +58,6 @@ jcl-over-slf4j ${org.slf4j.version} - - org.slf4j - log4j-over-slf4j - ${org.slf4j.version} - diff --git a/couchbase-sdk/src/main/resources/logback.xml b/couchbase-sdk/src/main/resources/logback.xml index efcc6fb4c771..ec0dc2469ae0 100644 --- a/couchbase-sdk/src/main/resources/logback.xml +++ b/couchbase-sdk/src/main/resources/logback.xml @@ -1,5 +1,5 @@ + - web - %date [%thread] %-5level %logger{36} - %message%n @@ -8,10 +8,12 @@ - + + + + - \ No newline at end of file diff --git a/couchbase-sdk/src/test/resources/logback.xml b/couchbase-sdk/src/test/resources/logback.xml index efcc6fb4c771..ec0dc2469ae0 100644 --- a/couchbase-sdk/src/test/resources/logback.xml +++ b/couchbase-sdk/src/test/resources/logback.xml @@ -1,5 +1,5 @@ + - web - %date [%thread] %-5level %logger{36} - %message%n @@ -8,10 +8,12 @@ - + + + + - \ No newline at end of file diff --git a/cucumber/README.md b/cucumber/README.md new file mode 100644 index 000000000000..5466d5c5611c --- /dev/null +++ b/cucumber/README.md @@ -0,0 +1,3 @@ +### Relevant articles + +- [Cucumber and Scenario Outline](http://www.baeldung.com/cucumber-scenario-outline) diff --git a/cucumber/pom.xml b/cucumber/pom.xml new file mode 100644 index 000000000000..77d04f96c2aa --- /dev/null +++ b/cucumber/pom.xml @@ -0,0 +1,93 @@ + + 4.0.0 + com.baeldung + cucumber + 1.0.0-SNAPSHOT + jar + + + 1.8 + 1.8 + UTF-8 + 3.5.1 + 1.7.21 + 1.1.7 + 4.12 + 1.3 + 1.2.5 + 2.19.1 + + + + + + + + info.cukes + cucumber-junit + ${cucumber.version} + test + + + + info.cukes + cucumber-java + ${cucumber.version} + test + + + + org.hamcrest + hamcrest-library + ${hamcrest.library.version} + test + + + + org.slf4j + slf4j-api + ${slf4j.version} + + + + ch.qos.logback + logback-classic + ${logback.version} + + + + ch.qos.logback + logback-core + ${logback.version} + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven.compiler.plugin.version} + + true + true + ${java.source.version} + ${java.target.version} + + + + org.apache.maven.plugins + maven-surefire-plugin + ${surefire.plugin.version} + + + **/CalculatorTest.java + + + + + + + \ No newline at end of file diff --git a/cucumber/src/main/java/com/baeldung/cucumber/calculator/Calculator.java b/cucumber/src/main/java/com/baeldung/cucumber/calculator/Calculator.java new file mode 100644 index 000000000000..fd4a3bad7b6b --- /dev/null +++ b/cucumber/src/main/java/com/baeldung/cucumber/calculator/Calculator.java @@ -0,0 +1,7 @@ +package com.baeldung.cucumber.calculator; + +public class Calculator { + public int add(int a, int b) { + return a + b; + } +} diff --git a/cucumber/src/test/java/com/baeldung/cucumber/calculator/CalculatorRunSteps.java b/cucumber/src/test/java/com/baeldung/cucumber/calculator/CalculatorRunSteps.java new file mode 100644 index 000000000000..9c0e920a8d0a --- /dev/null +++ b/cucumber/src/test/java/com/baeldung/cucumber/calculator/CalculatorRunSteps.java @@ -0,0 +1,38 @@ +package com.baeldung.cucumber.calculator; + +import org.hamcrest.Matchers; +import org.junit.Assert; + +import cucumber.api.java.Before; +import cucumber.api.java.en.Given; +import cucumber.api.java.en.Then; +import cucumber.api.java.en.When; + +public class CalculatorRunSteps { + + private int total; + + private Calculator calculator; + + @Before + private void init() { + total = -999; + + } + + @Given("^I have a calculator$") + public void initializeCalculator() throws Throwable { + calculator = new Calculator(); + } + + @When("^I add (-?\\d+) and (-?\\d+)$") + public void testAdd(int num1, int num2) throws Throwable { + total = calculator.add(num1, num2); + } + + @Then("^the result should be (-?\\d+)$") + public void validateResult(int result) throws Throwable { + Assert.assertThat(total, Matchers.equalTo(result)); + } + +} diff --git a/cucumber/src/test/java/com/baeldung/cucumber/calculator/CalculatorTest.java b/cucumber/src/test/java/com/baeldung/cucumber/calculator/CalculatorTest.java new file mode 100644 index 000000000000..6bbbca60d2e7 --- /dev/null +++ b/cucumber/src/test/java/com/baeldung/cucumber/calculator/CalculatorTest.java @@ -0,0 +1,15 @@ +package com.baeldung.cucumber.calculator; + +import org.junit.runner.RunWith; + +import cucumber.api.CucumberOptions; +import cucumber.api.junit.Cucumber; + +@RunWith(Cucumber.class) +@CucumberOptions( + features={"classpath:features/calculator.feature", "classpath:features/calculator-scenario-outline.feature"} + , plugin = { "pretty", "json:target/reports/json/calculator.json" } + , glue = {"com.baeldung.cucumber.calculator"} +) +public class CalculatorTest { +} diff --git a/cucumber/src/test/resources/features/calculator-scenario-outline.feature b/cucumber/src/test/resources/features/calculator-scenario-outline.feature new file mode 100644 index 000000000000..7437dbf5f95f --- /dev/null +++ b/cucumber/src/test/resources/features/calculator-scenario-outline.feature @@ -0,0 +1,16 @@ +Feature: Calculator + As a user + I want to use a calculator to add numbers + So that I don't need to add myself + + Scenario Outline: Add two numbers & + Given I have a calculator + When I add and + Then the result should be + + Examples: + | num1 | num2 | total | + | -2 | 3 | 1 | + | 10 | 15 | 25 | + | 99 | -99 | 0 | + | -1 | -10 | -11 | \ No newline at end of file diff --git a/cucumber/src/test/resources/features/calculator.feature b/cucumber/src/test/resources/features/calculator.feature new file mode 100644 index 000000000000..eaf05cb6ca9b --- /dev/null +++ b/cucumber/src/test/resources/features/calculator.feature @@ -0,0 +1,24 @@ +Feature: Calculator + As a user + I want to use a calculator to add numbers + So that I don't need to add myself + + Scenario: Add two numbers -2 & 3 + Given I have a calculator + When I add -2 and 3 + Then the result should be 1 + + Scenario: Add two numbers 10 & 15 + Given I have a calculator + When I add 10 and 15 + Then the result should be 25 + + Scenario: Add two numbers 99 & -99 + Given I have a calculator + When I add 99 and -99 + Then the result should be 0 + + Scenario: Add two numbers -1 & -10 + Given I have a calculator + When I add -1 and -10 + Then the result should be -11 \ No newline at end of file diff --git a/cucumber/src/test/resources/logback-test.xml b/cucumber/src/test/resources/logback-test.xml new file mode 100644 index 000000000000..e980d88693de --- /dev/null +++ b/cucumber/src/test/resources/logback-test.xml @@ -0,0 +1,13 @@ + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n%ex + + + + + + + + + \ No newline at end of file diff --git a/deltaspike/pom.xml b/deltaspike/pom.xml index 141b5b0da6e2..8135ced13a7e 100644 --- a/deltaspike/pom.xml +++ b/deltaspike/pom.xml @@ -201,12 +201,6 @@ querydsl-jpa ${querydsl.version} - - - org.slf4j - slf4j-log4j12 - ${slf4j.version} - diff --git a/disruptor/pom.xml b/disruptor/pom.xml index 7f2c78c9b017..865a87596ac2 100644 --- a/disruptor/pom.xml +++ b/disruptor/pom.xml @@ -40,11 +40,6 @@ jcl-over-slf4j ${org.slf4j.version} - - org.slf4j - log4j-over-slf4j - ${org.slf4j.version} - @@ -145,6 +140,7 @@ single + ${project.basedir} org.baeldung.executable.ExecutableMavenJar diff --git a/events/MessagesAggregate/0638124c-9a1b-4d25-8fce-cc223d472e77.events b/events/MessagesAggregate/0638124c-9a1b-4d25-8fce-cc223d472e77.events new file mode 100644 index 000000000000..d3ce8b9cea0d Binary files /dev/null and b/events/MessagesAggregate/0638124c-9a1b-4d25-8fce-cc223d472e77.events differ diff --git a/events/MessagesAggregate/d2ba9cbe-1a44-428e-a710-13b1bdc67c4b.events b/events/MessagesAggregate/d2ba9cbe-1a44-428e-a710-13b1bdc67c4b.events new file mode 100644 index 000000000000..2ab0ec469f90 Binary files /dev/null and b/events/MessagesAggregate/d2ba9cbe-1a44-428e-a710-13b1bdc67c4b.events differ diff --git a/events/ToDoItem/bf420ffc-0c3b-403e-bb8c-66cf499c773e.events b/events/ToDoItem/bf420ffc-0c3b-403e-bb8c-66cf499c773e.events new file mode 100644 index 000000000000..d805fc253e39 Binary files /dev/null and b/events/ToDoItem/bf420ffc-0c3b-403e-bb8c-66cf499c773e.events differ diff --git a/events/ToDoItem/e72a057b-adea-4c69-83a0-0431318823e7.events b/events/ToDoItem/e72a057b-adea-4c69-83a0-0431318823e7.events new file mode 100644 index 000000000000..3d67b74ced44 Binary files /dev/null and b/events/ToDoItem/e72a057b-adea-4c69-83a0-0431318823e7.events differ diff --git a/feign/pom.xml b/feign/pom.xml index 160f37ec2c6a..de086a3e4329 100644 --- a/feign/pom.xml +++ b/feign/pom.xml @@ -18,7 +18,6 @@ UTF-8 9.4.0 1.7.21 - 2.7 1.16.12 4.12 3.6.0 @@ -52,16 +51,6 @@ slf4j-api ${slf4j.version} - - org.apache.logging.log4j - log4j-core - ${log4j.version} - - - org.apache.logging.log4j - log4j-slf4j-impl - ${log4j.version} - org.projectlombok lombok diff --git a/feign/src/main/resources/log4j2.xml b/feign/src/main/resources/log4j2.xml deleted file mode 100644 index 659c5fda0e30..000000000000 --- a/feign/src/main/resources/log4j2.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/groovy-spock/README.md b/groovy-spock/README.md new file mode 100644 index 000000000000..18d26e8fc0b7 --- /dev/null +++ b/groovy-spock/README.md @@ -0,0 +1,3 @@ +### Relevant articles + +- [Introduction to Testing with Spock and Groovy](http://www.baeldung.com/groovy-spock) diff --git a/groovy-spock/pom.xml b/groovy-spock/pom.xml new file mode 100644 index 000000000000..be84500b0d1d --- /dev/null +++ b/groovy-spock/pom.xml @@ -0,0 +1,47 @@ + + + 4.0.0 + org.spockframework + groovy-spock + 1.0-SNAPSHOT + jar + Spock Framework - Example Project + + + UTF-8 + UTF-8 + + + + + + org.codehaus.gmavenplus + gmavenplus-plugin + 1.5 + + + + compile + testCompile + + + + + + + + + + org.spockframework + spock-core + 1.0-groovy-2.4 + test + + + org.codehaus.groovy + groovy-all + 2.4.7 + + + \ No newline at end of file diff --git a/groovy-spock/src/test/groovy/FirstSpecification.groovy b/groovy-spock/src/test/groovy/FirstSpecification.groovy new file mode 100644 index 000000000000..ed228899a29f --- /dev/null +++ b/groovy-spock/src/test/groovy/FirstSpecification.groovy @@ -0,0 +1,89 @@ +import spock.lang.Specification + +class FirstSpecification extends Specification { + + def "one plus one should equal two"() { + expect: + 1 + 1 == 2 + } + + def "two plus two should equal four"() { + given: + int left = 2 + int right = 2 + + when: + int result = left + right + + then: + result == 4 + } + + def "Should be able to remove from list"() { + given: + def list = [1, 2, 3, 4] + + when: + list.remove(0) + + then: + list == [2, 3, 4] + } + + def "Should get an index out of bounds when removing a non-existent item"() { + given: + def list = [1, 2, 3, 4] + + when: + list.remove(20) + + then: + thrown(IndexOutOfBoundsException) + list.size() == 4 + } + + def "numbers to the power of two"(int a, int b, int c) { + expect: + Math.pow(a, b) == c + + where: + a | b | c + 1 | 2 | 1 + 2 | 2 | 4 + 3 | 2 | 9 + } + + def "Should return default value for mock"() { + given: + def paymentGateway = Mock(PaymentGateway) + + when: + def result = paymentGateway.makePayment(12.99) + + then: + !result + } + + def "Should return true value for mock"() { + given: + def paymentGateway = Mock(PaymentGateway) + paymentGateway.makePayment(20) >> true + + when: + def result = paymentGateway.makePayment(20) + + then: + result + } + + def "Should verify notify was called"() { + given: + def notifier = Mock(Notifier) + + when: + notifier.notify('foo') + + then: + 1 * notifier.notify('foo') + } +} diff --git a/groovy-spock/src/test/groovy/Notifier.groovy b/groovy-spock/src/test/groovy/Notifier.groovy new file mode 100644 index 000000000000..d92d0f86ef77 --- /dev/null +++ b/groovy-spock/src/test/groovy/Notifier.groovy @@ -0,0 +1,3 @@ +interface Notifier { + void notify(String message) +} \ No newline at end of file diff --git a/groovy-spock/src/test/groovy/PaymentGateway.groovy b/groovy-spock/src/test/groovy/PaymentGateway.groovy new file mode 100644 index 000000000000..2a66e050f980 --- /dev/null +++ b/groovy-spock/src/test/groovy/PaymentGateway.groovy @@ -0,0 +1,3 @@ +interface PaymentGateway { + boolean makePayment(BigDecimal amount) +} \ No newline at end of file diff --git a/gson/src/main/resources/logback.xml b/gson/src/main/resources/logback.xml index 1146dade632e..ec0dc2469ae0 100644 --- a/gson/src/main/resources/logback.xml +++ b/gson/src/main/resources/logback.xml @@ -1,20 +1,19 @@ + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - + + - - - - - - - - - + + + + + \ No newline at end of file diff --git a/guava/README.md b/guava/README.md index ee224bae5ffd..f46c4dd3deb3 100644 --- a/guava/README.md +++ b/guava/README.md @@ -24,3 +24,4 @@ - [Guide to Guava RangeSet](http://www.baeldung.com/guava-rangeset) - [Guide to Guava RangeMap](http://www.baeldung.com/guava-rangemap) - [Guide to Guava Table](http://www.baeldung.com/guava-table) +- [Guide to Guava’s Reflection Utilities](http://www.baeldung.com/guava-reflection) diff --git a/guava/pom.xml b/guava/pom.xml index 0edbb90efd7c..447ee47cfc8b 100644 --- a/guava/pom.xml +++ b/guava/pom.xml @@ -76,11 +76,6 @@ ${org.slf4j.version} - - org.slf4j - log4j-over-slf4j - ${org.slf4j.version} - diff --git a/guava/src/main/resources/logback.xml b/guava/src/main/resources/logback.xml index 1146dade632e..ec0dc2469ae0 100644 --- a/guava/src/main/resources/logback.xml +++ b/guava/src/main/resources/logback.xml @@ -1,20 +1,19 @@ + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - + + - - - - - - - - - + + + + + \ No newline at end of file diff --git a/guava/src/test/java/org/baeldung/guava/GuavaMathTest.java b/guava/src/test/java/org/baeldung/guava/GuavaMathTest.java new file mode 100644 index 000000000000..b8e13f065020 --- /dev/null +++ b/guava/src/test/java/org/baeldung/guava/GuavaMathTest.java @@ -0,0 +1,220 @@ +package org.baeldung.guava; + +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.*; + +import java.math.BigInteger; +import java.math.RoundingMode; + +import org.junit.Test; + +import com.google.common.math.DoubleMath; +import com.google.common.math.IntMath; + +public class GuavaMathTest { + @Test(expected = ArithmeticException.class) + public void whenSumOverflow_thenThrowException() { + IntMath.checkedAdd(Integer.MAX_VALUE, 1); + } + + @Test(expected = ArithmeticException.class) + public void whenSumUnderflow_thenThrowException() { + IntMath.checkedAdd(Integer.MIN_VALUE, -1); + } + + @Test + public void should_calculate_sum() { + int result = IntMath.checkedAdd(2, 1); + assertThat(result, equalTo(3)); + } + + @Test + public void whenSumOverflow_thenReturnMaxInteger() { + int result = IntMath.saturatedAdd(Integer.MAX_VALUE, 100); + assertThat(result, equalTo(Integer.MAX_VALUE)); + } + + @Test + public void whenSumUnderflow_thenReturnMinInteger() { + int result = IntMath.saturatedAdd(Integer.MIN_VALUE, -100); + assertThat(result, equalTo(Integer.MIN_VALUE)); + } + + @Test(expected = ArithmeticException.class) + public void whenDifferenceOverflow_thenThrowException() { + IntMath.checkedSubtract(Integer.MAX_VALUE, -1); + } + + @Test(expected = ArithmeticException.class) + public void whenDifferenceUnderflow_thenThrowException() { + IntMath.checkedSubtract(Integer.MIN_VALUE, 1); + } + + @Test + public void should_calculate_difference() { + int result = IntMath.checkedSubtract(200, 100); + assertThat(result, equalTo(100)); + } + + @Test + public void whenDifferenceOverflow_thenReturnMaxInteger() { + int result = IntMath.saturatedSubtract(Integer.MAX_VALUE, -1); + assertThat(result, equalTo(Integer.MAX_VALUE)); + } + + @Test + public void whenDifferenceUnderflow_thenReturnMinInteger() { + int result = IntMath.saturatedSubtract(Integer.MIN_VALUE, 1); + assertThat(result, equalTo(Integer.MIN_VALUE)); + } + + @Test(expected = ArithmeticException.class) + public void whenProductOverflow_thenThrowException() { + IntMath.checkedMultiply(Integer.MAX_VALUE, 2); + } + + @Test(expected = ArithmeticException.class) + public void whenProductUnderflow_thenThrowException() { + IntMath.checkedMultiply(Integer.MIN_VALUE, 2); + } + + @Test + public void should_calculate_product() { + int result = IntMath.checkedMultiply(21, 3); + assertThat(result, equalTo(63)); + } + + @Test + public void whenProductOverflow_thenReturnMaxInteger() { + int result = IntMath.saturatedMultiply(Integer.MAX_VALUE, 2); + assertThat(result, equalTo(Integer.MAX_VALUE)); + } + + @Test + public void whenProductUnderflow_thenReturnMinInteger() { + int result = IntMath.saturatedMultiply(Integer.MIN_VALUE, 2); + assertThat(result, equalTo(Integer.MIN_VALUE)); + } + + @Test(expected = ArithmeticException.class) + public void whenPowerOverflow_thenThrowException() { + IntMath.checkedPow(Integer.MAX_VALUE, 2); + } + + @Test(expected = ArithmeticException.class) + public void whenPowerUnderflow_thenThrowException() { + IntMath.checkedPow(Integer.MIN_VALUE, 3); + } + + @Test + public void should_calculate_power() { + int result = IntMath.saturatedPow(3, 3); + assertThat(result, equalTo(27)); + } + + @Test + public void whenPowerOverflow_thenReturnMaxInteger() { + int result = IntMath.saturatedPow(Integer.MAX_VALUE, 2); + assertThat(result, equalTo(Integer.MAX_VALUE)); + } + + @Test + public void whenPowerUnderflow_thenReturnMinInteger() { + int result = IntMath.saturatedPow(Integer.MIN_VALUE, 3); + assertThat(result, equalTo(Integer.MIN_VALUE)); + } + + @Test + public void should_round_divide_result() { + int result1 = IntMath.divide(3, 2, RoundingMode.DOWN); + assertThat(result1, equalTo(1)); + + int result2 = IntMath.divide(3, 2, RoundingMode.UP); + assertThat(result2, equalTo(2)); + } + + @Test + public void should_round_log2_result() { + int result1 = IntMath.log2(5, RoundingMode.FLOOR); + assertThat(result1, equalTo(2)); + + int result2 = IntMath.log2(5, RoundingMode.CEILING); + assertThat(result2, equalTo(3)); + } + + @Test + public void should_round_log10_result() { + int result = IntMath.log10(11, RoundingMode.HALF_UP); + assertThat(result, equalTo(1)); + } + + @Test + public void should_round_sqrt_result() { + int result = IntMath.sqrt(4, RoundingMode.UNNECESSARY); + assertThat(result, equalTo(2)); + } + + @Test(expected = ArithmeticException.class) + public void whenNeedRounding_thenThrowException() { + IntMath.sqrt(5, RoundingMode.UNNECESSARY); + } + + @Test + public void should_calculate_gcd() { + int result = IntMath.gcd(15, 20); + assertThat(result, equalTo(5)); + } + + @Test + public void should_calculate_mod() { + int result = IntMath.mod(8, 3); + assertThat(result, equalTo(2)); + } + + @Test + public void should_test_if_is_power_of_two() { + boolean result1 = IntMath.isPowerOfTwo(8); + assertTrue(result1); + + boolean result2 = IntMath.isPowerOfTwo(9); + assertFalse(result2); + } + + @Test + public void should_calculate_factorial() { + int result = IntMath.factorial(4); + assertThat(result, equalTo(24)); + } + + @Test + public void should_calculate_binomial() { + int result = IntMath.binomial(7, 3); + assertThat(result, equalTo(35)); + } + + @Test + public void should_detect_integer() { + boolean result1 = DoubleMath.isMathematicalInteger(2.0); + assertThat(result1, equalTo(true)); + boolean result2 = DoubleMath.isMathematicalInteger(2.1); + assertThat(result2, equalTo(false)); + } + + @Test + public void should_round_to_integer_types() { + int result3 = DoubleMath.roundToInt(2.5, RoundingMode.DOWN); + assertThat(result3, equalTo(2)); + + long result4 = DoubleMath.roundToLong(2.5, RoundingMode.HALF_UP); + assertThat(result4, equalTo(3L)); + + BigInteger result5 = DoubleMath.roundToBigInteger(2.5, RoundingMode.UP); + assertThat(result5, equalTo(new BigInteger("3"))); + } + + @Test + public void should_calculate_log_2() { + int result6 = DoubleMath.log2(10, RoundingMode.UP); + assertThat(result6, equalTo(4)); + } +} \ No newline at end of file diff --git a/guava/src/test/java/org/baeldung/guava/GuavaReflectionUtilsTest.java b/guava/src/test/java/org/baeldung/guava/GuavaReflectionUtilsTest.java new file mode 100644 index 000000000000..e59a682e080f --- /dev/null +++ b/guava/src/test/java/org/baeldung/guava/GuavaReflectionUtilsTest.java @@ -0,0 +1,150 @@ +package org.baeldung.guava; + + +import com.google.common.collect.Lists; +import com.google.common.reflect.Invokable; +import com.google.common.reflect.TypeToken; +import org.junit.Test; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; + +import static junit.framework.TestCase.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class GuavaReflectionUtilsTest { + + @Test + public void givenTwoGenericList_whenCheckIsAssignableFrom_thenReturnTrueDueToTypeErasure() { + //given + ArrayList stringList = Lists.newArrayList(); + ArrayList intList = Lists.newArrayList(); + + //when + boolean result = stringList.getClass().isAssignableFrom(intList.getClass()); + + //then + assertTrue(result); + } + + @Test + public void givenTypeToken_whenResolveType_thenShouldResolveProperType() { + //given + TypeToken> stringListToken = new TypeToken>() { + }; + TypeToken> integerListToken = new TypeToken>() { + }; + TypeToken> numberTypeToken = new TypeToken>() { + }; + + //then + assertFalse(stringListToken.isSubtypeOf(integerListToken)); + assertFalse(numberTypeToken.isSubtypeOf(integerListToken)); + assertTrue(integerListToken.isSubtypeOf(numberTypeToken)); + } + + @Test + public void givenCustomClass_whenCaptureGeneric_thenReturnTypeAtRuntime() { + //given + ParametrizedClass parametrizedClass = new ParametrizedClass() { + }; + + //then + assertEquals(parametrizedClass.type, TypeToken.of(String.class)); + } + + @Test + public void givenComplexType_whenGetTypeArgument_thenShouldReturnTypeAtRuntime() { + //given + TypeToken> funToken = new TypeToken>() { + }; + + //when + TypeToken funResultToken = funToken.resolveType(Function.class.getTypeParameters()[1]); + + //then + assertEquals(funResultToken, TypeToken.of(String.class)); + } + + + @Test + public void givenMapType_whenGetTypeArgumentOfEntry_thenShouldReturnTypeAtRuntime() throws NoSuchMethodException { + //given + TypeToken> mapToken = new TypeToken>() { + }; + + //when + TypeToken entrySetToken = mapToken.resolveType(Map.class.getMethod("entrySet").getGenericReturnType()); + + //then + assertEquals(entrySetToken, new TypeToken>>() { + }); + } + + @Test + public void givenInvokable_whenCheckPublicMethod_shouldReturnTrue() throws NoSuchMethodException { + //given + Method method = CustomClass.class.getMethod("somePublicMethod"); + Invokable invokable = new TypeToken() { + }.method(method); + + //when + boolean isPublicStandradJava = Modifier.isPublic(method.getModifiers()); + boolean isPublicGuava = invokable.isPublic(); + //then + assertTrue(isPublicStandradJava); + assertTrue(isPublicGuava); + } + + @Test + public void givenInvokable_whenCheckFinalMethod_shouldReturnFalseForIsOverridable() throws NoSuchMethodException { + //given + Method method = CustomClass.class.getMethod("notOverridablePublicMethod"); + Invokable invokable = new TypeToken() { + }.method(method); + + //when + boolean isOverridableStandardJava = (!(Modifier.isFinal(method.getModifiers()) || Modifier.isPrivate(method.getModifiers()) + || Modifier.isStatic(method.getModifiers()) + || Modifier.isFinal(method.getDeclaringClass().getModifiers()))); + boolean isOverridableFinalGauava = invokable.isOverridable(); + + //then + assertFalse(isOverridableStandardJava); + assertFalse(isOverridableFinalGauava); + } + + @Test + public void givenListOfType_whenGetReturnRype_shouldCaptureTypeAtRuntime() throws NoSuchMethodException { + //given + Method getMethod = List.class.getMethod("get", int.class); + + //when + Invokable, ?> invokable = new TypeToken>() { + }.method(getMethod); + + //then + assertEquals(TypeToken.of(Integer.class), invokable.getReturnType()); // Not Object.class! + } + + + abstract class ParametrizedClass { + TypeToken type = new TypeToken(getClass()) { + }; + } + + class CustomClass { + public void somePublicMethod() { + } + + public final void notOverridablePublicMethod() { + + } + } +} diff --git a/guava21/README.md b/guava21/README.md new file mode 100644 index 000000000000..8725352d69b4 --- /dev/null +++ b/guava21/README.md @@ -0,0 +1,2 @@ +### Relevant articles: +- [New Stream, Comparator and Collector Functionality in Guava 21](http://www.baeldung.com/guava-21-new) diff --git a/guava21/pom.xml b/guava21/pom.xml new file mode 100644 index 000000000000..f393c65aec50 --- /dev/null +++ b/guava21/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + com.baeldung.guava + tutorial + 1.0-SNAPSHOT + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + + + + + + com.google.guava + guava + 21.0 + + + + + junit + junit + 4.11 + + + + + + \ No newline at end of file diff --git a/guava21/src/main/java/com/baeldung/guava/tutorial/AtomicLongMapTutorials.java b/guava21/src/main/java/com/baeldung/guava/tutorial/AtomicLongMapTutorials.java new file mode 100644 index 000000000000..79ce02b7f02c --- /dev/null +++ b/guava21/src/main/java/com/baeldung/guava/tutorial/AtomicLongMapTutorials.java @@ -0,0 +1,27 @@ +package com.baeldung.guava.tutorial; + +import com.google.common.util.concurrent.AtomicLongMap; + +public class AtomicLongMapTutorials { + + private AtomicLongMap atomicLongMap; + + public AtomicLongMapTutorials() { + atomicLongMap = AtomicLongMap.create(); + } + + public void addKeys() { + atomicLongMap.addAndGet("apple", 250); + atomicLongMap.addAndGet("bat", 350); + atomicLongMap.addAndGet("cat", 450); + atomicLongMap.addAndGet("dog", 550); + } + + public static void main(String[] args) { + AtomicLongMapTutorials atomicLongMapTutorials = new AtomicLongMapTutorials(); + atomicLongMapTutorials.addKeys(); + + System.out.println(atomicLongMapTutorials.atomicLongMap.get("2")); + } + +} diff --git a/guava21/src/main/java/com/baeldung/guava/tutorial/ComparatorsExamples.java b/guava21/src/main/java/com/baeldung/guava/tutorial/ComparatorsExamples.java new file mode 100644 index 000000000000..abb4c51e8f2e --- /dev/null +++ b/guava21/src/main/java/com/baeldung/guava/tutorial/ComparatorsExamples.java @@ -0,0 +1,25 @@ +package com.baeldung.guava.tutorial; + +import com.google.common.collect.Comparators; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +public class ComparatorsExamples { + + public static void main(String[] args) { + + List integers = Arrays.asList(1, 2, 3, 4, 4, 6, 7, 8, 9, 10); + boolean isInAscendingOrder = Comparators.isInOrder(integers, new AscedingOrderComparator()); + System.out.println(isInAscendingOrder); + + } + + private static class AscedingOrderComparator implements Comparator { + @Override + public int compare(Integer o1, Integer o2) { + return o1.compareTo(o2); + } + } +} diff --git a/guava21/src/main/java/com/baeldung/guava/tutorial/ConcatStreams.java b/guava21/src/main/java/com/baeldung/guava/tutorial/ConcatStreams.java new file mode 100644 index 000000000000..0304d48fbc58 --- /dev/null +++ b/guava21/src/main/java/com/baeldung/guava/tutorial/ConcatStreams.java @@ -0,0 +1,11 @@ +package com.baeldung.guava.tutorial; + +import com.google.common.collect.Streams; + +import java.util.stream.Stream; + +public class ConcatStreams { + public static Stream concatStreams(Stream stream1, Stream stream2, Stream stream3) { + return Streams.concat(stream1, stream2, stream3); + } +} diff --git a/guava21/src/main/java/com/baeldung/guava/tutorial/InternerBuilderExample.java b/guava21/src/main/java/com/baeldung/guava/tutorial/InternerBuilderExample.java new file mode 100644 index 000000000000..5c85e684d59d --- /dev/null +++ b/guava21/src/main/java/com/baeldung/guava/tutorial/InternerBuilderExample.java @@ -0,0 +1,15 @@ +package com.baeldung.guava.tutorial; + +import com.google.common.collect.Interner; +import com.google.common.collect.Interners; + +public class InternerBuilderExample { + + public static void main(String[] args) { + Interner interners = Interners. newBuilder() + .concurrencyLevel(2) + .strong(). build(); + + } + +} diff --git a/guava21/src/main/java/com/baeldung/guava/tutorial/MonitorExample.java b/guava21/src/main/java/com/baeldung/guava/tutorial/MonitorExample.java new file mode 100644 index 000000000000..78bcbe3d49e8 --- /dev/null +++ b/guava21/src/main/java/com/baeldung/guava/tutorial/MonitorExample.java @@ -0,0 +1,27 @@ +package com.baeldung.guava.tutorial; + +import com.google.common.util.concurrent.Monitor; + +import java.util.ArrayList; +import java.util.List; + +public class MonitorExample { + private List students = new ArrayList(); + private static final int MAX_SIZE = 100; + + private Monitor monitor = new Monitor(); + + public void addToCourse(String item) throws InterruptedException { + Monitor.Guard studentsBelowCapacity = monitor.newGuard(this::isStudentsCapacityUptoLimit); + monitor.enterWhen(studentsBelowCapacity); + try { + students.add(item); + } finally { + monitor.leave(); + } + } + + public Boolean isStudentsCapacityUptoLimit() { + return students.size() > MAX_SIZE; + } +} diff --git a/guava21/src/main/java/com/baeldung/guava/tutorial/MoreCollectorsExample.java b/guava21/src/main/java/com/baeldung/guava/tutorial/MoreCollectorsExample.java new file mode 100644 index 000000000000..2b3bd7cdc49c --- /dev/null +++ b/guava21/src/main/java/com/baeldung/guava/tutorial/MoreCollectorsExample.java @@ -0,0 +1,18 @@ +package com.baeldung.guava.tutorial; + +import com.google.common.collect.MoreCollectors; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +public class MoreCollectorsExample { + + public static void main(String[] args) { + List numbers = Arrays.asList(1); + Optional number = numbers + .stream() + .map(e -> e * 2) + .collect(MoreCollectors.toOptional()); + } +} diff --git a/guava21/src/main/java/com/baeldung/guava/tutorial/StreamsUtility.java b/guava21/src/main/java/com/baeldung/guava/tutorial/StreamsUtility.java new file mode 100644 index 000000000000..b15f61afd56d --- /dev/null +++ b/guava21/src/main/java/com/baeldung/guava/tutorial/StreamsUtility.java @@ -0,0 +1,39 @@ +package com.baeldung.guava.tutorial; + +import com.google.common.collect.Streams; + +import java.util.*; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +public class StreamsUtility { + + public static void main(String[] args) { + + List numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20); + //Using Collection + Stream streamFromCollection = Streams.stream(numbers); + //Using Iterator + Stream streamFromIterator = Streams.stream(numbers.iterator()); + //Using Iterable + Stream streamFromIterable = Streams.stream((Iterable) numbers); + //Using Optional + Stream streamFromOptional = Streams.stream(Optional.of(1)); + //Using OptionalLong to LongStream + LongStream streamFromOptionalLong = Streams.stream(OptionalLong.of(1)); + //Using OptionalInt to IntStream + IntStream streamFromOptionalInt = Streams.stream(OptionalInt.of(1)); + //Using OptionalDouble to DoubleStream + DoubleStream streamFromOptionalDouble = Streams.stream(OptionalDouble.of(1.0)); + + Stream concatenatedStreams = Streams.concat(streamFromCollection, streamFromIterable, streamFromIterator); + + List integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + //This will return 10 + Optional lastItem = Streams.findLast(integers.stream()); + + Streams.zip(Stream.of("candy", "chocolate", "bar"), Stream.of("$1", "$2", "$3"), (arg1, arg2) -> arg1 + ":" + arg2); + } +} diff --git a/guava21/src/test/java/AtomicLongMapTests.java b/guava21/src/test/java/AtomicLongMapTests.java new file mode 100644 index 000000000000..6bde997e8da0 --- /dev/null +++ b/guava21/src/test/java/AtomicLongMapTests.java @@ -0,0 +1,54 @@ +import com.google.common.util.concurrent.AtomicLongMap; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class AtomicLongMapTests { + + private static final String SPRING_COURSE_KEY = "Spring"; + private static final String HIBERNATE_COURSE_KEY = "hibernate"; + private static final String GUAVA_COURSE_KEY = "Guava"; + + AtomicLongMap courses = AtomicLongMap.create(); + + public void setUp() { + courses.put(SPRING_COURSE_KEY, 1056); + courses.put(HIBERNATE_COURSE_KEY, 259); + courses.put(GUAVA_COURSE_KEY, 78); + } + + @Test + public void accumulateAndGet_withLongBinaryOperator_thenSuccessful() { + long noOfStudents = 56; + long oldValue = courses.get(SPRING_COURSE_KEY); + + long totalNotesRequired = courses.accumulateAndGet("Guava", noOfStudents, (x, y) -> (x * y)); + + assertEquals(totalNotesRequired, oldValue * noOfStudents); + } + + @Test + public void getAndAccumulate_withLongBinaryOperator_thenSuccessful() { + long noOfStudents = 56; + long beforeUpdate = courses.get(SPRING_COURSE_KEY); + + long onUpdate = courses.accumulateAndGet("Guava", noOfStudents, (x, y) -> (x * y)); + + long afterUpdate = courses.get(SPRING_COURSE_KEY); + + assertEquals(onUpdate, afterUpdate); + assertEquals(afterUpdate, beforeUpdate * noOfStudents); + } + + @Test + public void updateAndGet_withLongUnaryOperator_thenSuccessful() { + long beforeUpdate = courses.get(SPRING_COURSE_KEY); + + long onUpdate = courses.updateAndGet("Guava", (x) -> (x / 2)); + + long afterUpdate = courses.get(SPRING_COURSE_KEY); + + assertEquals(onUpdate, afterUpdate); + assertEquals(afterUpdate, beforeUpdate / 2); + } +} diff --git a/guava21/src/test/java/ComparatorsUnitTests.java b/guava21/src/test/java/ComparatorsUnitTests.java new file mode 100644 index 000000000000..8aaae1e14ef1 --- /dev/null +++ b/guava21/src/test/java/ComparatorsUnitTests.java @@ -0,0 +1,77 @@ +import com.google.common.collect.Comparators; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.function.Function; +import java.util.function.ToDoubleFunction; +import java.util.function.ToIntFunction; +import java.util.function.ToLongFunction; + +public class ComparatorsUnitTests { + + @Test + public void isInOrderTest() { + + List numbers = Arrays.asList(1, 2, 3, 4, 4, 6, 7, 8, 9, 10); + + boolean isInAscendingOrder = Comparators.isInOrder(numbers, new AscendingOrderComparator()); + + Assert.assertTrue(isInAscendingOrder); + } + + @Test + public void isInStrictOrderTest() { + + List numbers = Arrays.asList(1, 2, 3, 4, 3, 6, 7, 8, 9, 10); + + boolean isInAscendingOrder = Comparators.isInOrder(numbers, new AscendingOrderComparator()); + + Assert.assertFalse(isInAscendingOrder); + } + + private class AscendingOrderComparator implements Comparator { + + @Override + public int compare(Integer o1, Integer o2) { + return o1.compareTo(o2); + } + + @Override + public Comparator reversed() { + return null; + } + + @Override + public Comparator thenComparing(Comparator other) { + return null; + } + + @Override + public Comparator thenComparing(Function keyExtractor, Comparator keyComparator) { + return null; + } + + @Override + public > Comparator thenComparing(Function keyExtractor) { + return null; + } + + @Override + public Comparator thenComparingInt(ToIntFunction keyExtractor) { + return null; + } + + @Override + public Comparator thenComparingLong(ToLongFunction keyExtractor) { + return null; + } + + @Override + public Comparator thenComparingDouble(ToDoubleFunction keyExtractor) { + return null; + } + } +} diff --git a/guava21/src/test/java/GauavaStreamsTests.java b/guava21/src/test/java/GauavaStreamsTests.java new file mode 100644 index 000000000000..1482d685cf69 --- /dev/null +++ b/guava21/src/test/java/GauavaStreamsTests.java @@ -0,0 +1,158 @@ +import com.google.common.collect.Streams; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.*; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +public class GauavaStreamsTests { + + List numbers; + + @Before + public void setUp() { + numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20); + } + + @Test + public void createStreamsWithCollection() { + //Deprecated API to create stream from collection + Stream streamFromCollection = Streams.stream(numbers); + + //Assert.assertNotNull(streamFromCollection); + StreamUtility.assertStreamEquals(streamFromCollection, numbers.stream()); + } + + @Test + public void createStreamsWithIterable() { + Iterable numbersIterable = (Iterable) numbers; + + Stream streamFromIterable = Streams.stream(numbersIterable); + + Assert.assertNotNull(streamFromIterable); + StreamUtility.assertStreamEquals(streamFromIterable, numbers.stream()); + } + + @Test + public void createStreamsWithIterator() { + Iterator numbersIterator = numbers.iterator(); + + Stream streamFromIterator = Streams.stream(numbersIterator); + + Assert.assertNotNull(streamFromIterator); + StreamUtility.assertStreamEquals(streamFromIterator, numbers.stream()); + } + + @Test + public void createStreamsWithOptional() { + + Stream streamFromOptional = Streams.stream(Optional.of(1)); + + Assert.assertNotNull(streamFromOptional); + Assert.assertEquals(streamFromOptional.count(), 1); + } + + @Test + public void createStreamsWithOptionalLong() { + + LongStream streamFromOptionalLong = Streams.stream(OptionalLong.of(1)); + + Assert.assertNotNull(streamFromOptionalLong); + Assert.assertEquals(streamFromOptionalLong.count(), 1); + } + + @Test + public void createStreamsWithOptionalInt() { + + IntStream streamFromOptionalInt = Streams.stream(OptionalInt.of(1)); + + //Assert.assertNotNull(streamFromOptionalInt); + Assert.assertEquals(streamFromOptionalInt.count(), 1); + } + + @Test + public void createStreamsWithOptionalDouble() { + + DoubleStream streamFromOptionalDouble = Streams.stream(OptionalDouble.of(1.0)); + + //Assert.assertNotNull(streamFromOptionalDouble); + Assert.assertEquals(streamFromOptionalDouble.count(), 1); + + } + + @Test + public void concatStreamsOfSameType() { + Stream oddNumbers = Arrays + .asList(1, 3, 5, 7, 9, 11, 13, 15, 17, 19) + .stream(); + Stream evenNumbers = Arrays + .asList(2, 4, 6, 8, 10, 12, 14, 16, 18, 20) + .stream(); + + Stream combinedStreams = Streams.concat(oddNumbers, evenNumbers); + + //Assert.assertNotNull(combinedStreams); + StreamUtility.assertStreamEquals(combinedStreams, Stream.concat(oddNumbers, evenNumbers)); + } + + @Test + public void concatStreamsOfTypeLongStream() { + LongStream firstTwenty = LongStream.range(1, 20); + LongStream nextTwenty = LongStream.range(21, 40); + + LongStream combinedStreams = Streams.concat(firstTwenty, nextTwenty); + + Assert.assertNotNull(combinedStreams); + StreamUtility.assertStreamEquals(combinedStreams, LongStream.concat(firstTwenty, nextTwenty)); + } + + @Test + public void concatStreamsOfTypeIntStream() { + IntStream firstTwenty = IntStream.range(1, 20); + IntStream nextTwenty = IntStream.range(21, 40); + + IntStream combinedStreams = Streams.concat(firstTwenty, nextTwenty); + + Assert.assertNotNull(combinedStreams); + StreamUtility.assertStreamEquals(combinedStreams, IntStream.concat(firstTwenty, nextTwenty)); + } + + @Test + public void findLastOfStream() { + Optional lastElement = Streams.findLast(numbers.stream()); + + Assert.assertNotNull(lastElement.get()); + Assert.assertEquals(lastElement.get(), numbers.get(20)); + } + + @Test + public void mapWithIndexTest() { + Stream stringSream = Stream.of("a", "b", "c"); + + Stream mappedStream = Streams.mapWithIndex(stringSream, (str, index) -> str + ":" + index); + + //Assert.assertNotNull(mappedStream); + Assert.assertEquals(mappedStream + .findFirst() + .get(), "a:0"); + + } + + @Test + public void streamsZipTest() { + Stream stringSream = Stream.of("a", "b", "c"); + Stream intStream = Stream.of(1, 2, 3); + Stream mappedStream = Streams.zip(stringSream, intStream, (str, index) -> str + ":" + index); + + //Assert.assertNotNull(mappedStream); + Assert.assertEquals(mappedStream + .findFirst() + .get(), "a:1"); + + } + +} diff --git a/guava21/src/test/java/InternBuilderUnitTests.java b/guava21/src/test/java/InternBuilderUnitTests.java new file mode 100644 index 000000000000..b569b59978a4 --- /dev/null +++ b/guava21/src/test/java/InternBuilderUnitTests.java @@ -0,0 +1,18 @@ +import com.google.common.collect.Interner; +import com.google.common.collect.Interners; +import org.junit.Assert; +import org.junit.Test; + +public class InternBuilderUnitTests { + + @Test + public void interBuilderTest() { + + Interner interners = Interners. newBuilder() + .concurrencyLevel(2) + .strong(). build(); + + Assert.assertNotNull(interners); + } + +} diff --git a/guava21/src/test/java/MonitorUnitTests.java b/guava21/src/test/java/MonitorUnitTests.java new file mode 100644 index 000000000000..7b52c48d8fc0 --- /dev/null +++ b/guava21/src/test/java/MonitorUnitTests.java @@ -0,0 +1,53 @@ +import com.google.common.util.concurrent.Monitor; +import org.junit.Assert; +import org.junit.Test; + +public class MonitorUnitTests { + + @Test + public void whenGaurdConditionIsTrue_IsSuccessful() { + Monitor monitor = new Monitor(); + boolean enteredInCriticalSection = false; + + Monitor.Guard gaurdCondition = monitor.newGuard(this::returnTrue); + + if (monitor.enterIf(gaurdCondition)) { + try { + System.out.println("Entered in critical section"); + enteredInCriticalSection = true; + } finally { + monitor.leave(); + } + } + + Assert.assertTrue(enteredInCriticalSection); + + } + + @Test + public void whenGaurdConditionIsFalse_IsSuccessful() { + Monitor monitor = new Monitor(); + boolean enteredInCriticalSection = false; + + Monitor.Guard gaurdCondition = monitor.newGuard(this::returnFalse); + + if (monitor.enterIf(gaurdCondition)) { + try { + System.out.println("Entered in critical section"); + enteredInCriticalSection = true; + } finally { + monitor.leave(); + } + } + + Assert.assertFalse(enteredInCriticalSection); + } + + private boolean returnTrue() { + return true; + } + + private boolean returnFalse() { + return false; + } +} diff --git a/guava21/src/test/java/MoreCollectorsUnitTests.java b/guava21/src/test/java/MoreCollectorsUnitTests.java new file mode 100644 index 000000000000..a205337450a6 --- /dev/null +++ b/guava21/src/test/java/MoreCollectorsUnitTests.java @@ -0,0 +1,36 @@ +import com.google.common.collect.MoreCollectors; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +public class MoreCollectorsUnitTests { + + @Test + public void toOptionalTest() { + + List numbers = Arrays.asList(1); + + Optional number = numbers + .stream() + .map(e -> e * 2) + .collect(MoreCollectors.toOptional()); + + Assert.assertEquals(number.get(), new Integer(2)); + } + + @Test + public void onlyElementTest() { + List numbers = Arrays.asList(1); + + Integer number = numbers + .stream() + .map(e -> e * 2) + .collect(MoreCollectors.onlyElement()); + + Assert.assertEquals(number, new Integer(2)); + } + +} diff --git a/guava21/src/test/java/StreamUtility.java b/guava21/src/test/java/StreamUtility.java new file mode 100644 index 000000000000..1eb866fb88df --- /dev/null +++ b/guava21/src/test/java/StreamUtility.java @@ -0,0 +1,66 @@ +import org.junit.Assert; + +import java.util.Iterator; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +public class StreamUtility { + + public static boolean assertStreamEquals(Stream stream1, Stream stream2) { + + Iterator iterator1 = stream1.iterator(); + Iterator iterator2 = stream2.iterator(); + + while (iterator1.hasNext()) { + Assert.assertEquals(iterator1.next(), iterator2.next()); + } + + Assert.assertFalse(iterator2.hasNext()); + + return true; + } + + public static boolean assertStreamEquals(LongStream stream1, LongStream stream2) { + + Iterator iterator1 = stream1.iterator(); + Iterator iterator2 = stream2.iterator(); + + while (iterator1.hasNext()) { + Assert.assertEquals(iterator1.next(), iterator2.next()); + } + + Assert.assertFalse(iterator2.hasNext()); + + return true; + } + + public static boolean assertStreamEquals(DoubleStream stream1, DoubleStream stream2) { + + Iterator iterator1 = stream1.iterator(); + Iterator iterator2 = stream2.iterator(); + + while (iterator1.hasNext()) { + Assert.assertEquals(iterator1.next(), iterator2.next()); + } + + Assert.assertFalse(iterator2.hasNext()); + + return true; + } + + public static boolean assertStreamEquals(IntStream stream1, IntStream stream2) { + + Iterator iterator1 = stream1.iterator(); + Iterator iterator2 = stream2.iterator(); + + while (iterator1.hasNext()) { + Assert.assertEquals(iterator1.next(), iterator2.next()); + } + + Assert.assertFalse(iterator2.hasNext()); + + return true; + } +} diff --git a/guice/README.md b/guice/README.md new file mode 100644 index 000000000000..d1bd1ff8833c --- /dev/null +++ b/guice/README.md @@ -0,0 +1,4 @@ +## Google Guice Tutorials Project + +### Relevant Articles +- [Guide to Google Guice](http://www.baeldung.com/guice) diff --git a/guice/pom.xml b/guice/pom.xml new file mode 100644 index 000000000000..df870217943f --- /dev/null +++ b/guice/pom.xml @@ -0,0 +1,34 @@ + + + 4.0.0 + com.baeldung.examples.guice + guice + 1.0-SNAPSHOT + jar + + + com.google.inject + guice + ${guice.version} + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + + + UTF-8 + 1.8 + 1.8 + 4.1.0 + + guice + diff --git a/guice/src/main/java/com/baeldung/examples/RunGuice.java b/guice/src/main/java/com/baeldung/examples/RunGuice.java new file mode 100644 index 000000000000..660952f3259a --- /dev/null +++ b/guice/src/main/java/com/baeldung/examples/RunGuice.java @@ -0,0 +1,32 @@ + +package com.baeldung.examples; + +import com.baeldung.examples.guice.Communication; +import com.baeldung.examples.guice.binding.AOPModule; +import com.baeldung.examples.guice.modules.BasicModule; +import com.google.inject.Guice; +import com.google.inject.Injector; +import java.util.Scanner; + +/** + * + * @author baeldung + */ +public class RunGuice { + + public static void main(String[] args) { + Injector injector = Guice.createInjector(new BasicModule(), new AOPModule()); + Communication comms = injector.getInstance(Communication.class); + Scanner scanner = new Scanner(System.in); + while (true) { + String input = scanner.nextLine(); + if (input.equalsIgnoreCase("q")) { + System.exit(0); + } else { + comms.sendMessage(input); + } + + } + + } +} diff --git a/guice/src/main/java/com/baeldung/examples/guice/Communication.java b/guice/src/main/java/com/baeldung/examples/guice/Communication.java new file mode 100644 index 000000000000..464e0c641d0a --- /dev/null +++ b/guice/src/main/java/com/baeldung/examples/guice/Communication.java @@ -0,0 +1,40 @@ + +package com.baeldung.examples.guice; + +import com.google.inject.Inject; +import com.google.inject.name.Named; +import java.util.Date; +import java.util.LinkedList; +import java.util.Queue; +import java.util.logging.Logger; + +/** + * + * @author baeldung + */ +public class Communication { + + final Date start = new Date(); + + @Inject + private Logger logger; + + @Inject + private DefaultCommunicator communicator; + + public Communication(Boolean keepRecords) { + if (keepRecords) { + System.out.println("keeping records"); + } + } + + public boolean sendMessage(String message) { + + return communicator.sendMessage(message); + } + + public DefaultCommunicator getCommunicator() { + return this.communicator; + } + +} diff --git a/guice/src/main/java/com/baeldung/examples/guice/CommunicationMode.java b/guice/src/main/java/com/baeldung/examples/guice/CommunicationMode.java new file mode 100644 index 000000000000..7a36f0c276b3 --- /dev/null +++ b/guice/src/main/java/com/baeldung/examples/guice/CommunicationMode.java @@ -0,0 +1,12 @@ + +package com.baeldung.examples.guice; + +import com.baeldung.examples.guice.constant.CommunicationModel; + +public interface CommunicationMode { + + public CommunicationModel getMode(); + + public boolean sendMessage(String message); + +} diff --git a/guice/src/main/java/com/baeldung/examples/guice/DefaultCommunicator.java b/guice/src/main/java/com/baeldung/examples/guice/DefaultCommunicator.java new file mode 100644 index 000000000000..24e0c28dd1d4 --- /dev/null +++ b/guice/src/main/java/com/baeldung/examples/guice/DefaultCommunicator.java @@ -0,0 +1,51 @@ + +package com.baeldung.examples.guice; + +import com.baeldung.examples.guice.marker.Communicator; +import com.google.inject.Inject; +import com.google.inject.name.Named; + +/** + * + * @author baeldung + */ +public class DefaultCommunicator implements Communicator { + + private CommunicationMode defaultCommsMode; + @Inject + @Named("SMSComms") + CommunicationMode smsCommsMode; + @Inject + @Named("EmailComms") + CommunicationMode emailCommsMode; + @Inject + @Named("IMComms") + CommunicationMode imCommsMode; + + protected DefaultCommunicator(CommunicationMode defaultComms) { + this.defaultCommsMode = defaultComms; + } + + public DefaultCommunicator() { + + } + + public boolean sendMessage(String message) { + boolean sent = false; + if (defaultCommsMode != null) { + sent = sendMessageByDefault(message); + } else { + sent = smsCommsMode.sendMessage(message); + } + return sent; + } + + private boolean sendMessageByDefault(String message) { + boolean sent = false; + if (message != null && !message.trim().equals("")) { + return defaultCommsMode.sendMessage(message); + } + return sent; + } + +} diff --git a/guice/src/main/java/com/baeldung/examples/guice/EmailCommunicationMode.java b/guice/src/main/java/com/baeldung/examples/guice/EmailCommunicationMode.java new file mode 100644 index 000000000000..06e77a58e26e --- /dev/null +++ b/guice/src/main/java/com/baeldung/examples/guice/EmailCommunicationMode.java @@ -0,0 +1,24 @@ + +package com.baeldung.examples.guice; + +import com.baeldung.examples.guice.aop.MessageSentLoggable; +import com.baeldung.examples.guice.constant.CommunicationModel; + +/** + * + * @author baeldung + */ +public class EmailCommunicationMode implements CommunicationMode { + + @Override + public CommunicationModel getMode() { + return CommunicationModel.EMAIL; + } + + @Override + @MessageSentLoggable + public boolean sendMessage(String Message) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + +} diff --git a/guice/src/main/java/com/baeldung/examples/guice/IMCommunicationMode.java b/guice/src/main/java/com/baeldung/examples/guice/IMCommunicationMode.java new file mode 100644 index 000000000000..42b0c82b9047 --- /dev/null +++ b/guice/src/main/java/com/baeldung/examples/guice/IMCommunicationMode.java @@ -0,0 +1,30 @@ + +package com.baeldung.examples.guice; + +import com.baeldung.examples.guice.aop.MessageSentLoggable; +import com.baeldung.examples.guice.constant.CommunicationModel; +import com.google.inject.Inject; +import java.util.logging.Logger; + +/** + * + * @author baeldung + */ +public class IMCommunicationMode implements CommunicationMode { + + @Inject + private Logger logger; + + @Override + public CommunicationModel getMode() { + return CommunicationModel.IM; + } + + @Override + @MessageSentLoggable + public boolean sendMessage(String message) { + logger.info("IM Message Sent"); + return true; + } + +} diff --git a/guice/src/main/java/com/baeldung/examples/guice/SMSCommunicationMode.java b/guice/src/main/java/com/baeldung/examples/guice/SMSCommunicationMode.java new file mode 100644 index 000000000000..7a30e51f104d --- /dev/null +++ b/guice/src/main/java/com/baeldung/examples/guice/SMSCommunicationMode.java @@ -0,0 +1,30 @@ + +package com.baeldung.examples.guice; + +import com.baeldung.examples.guice.aop.MessageSentLoggable; +import com.baeldung.examples.guice.constant.CommunicationModel; +import com.google.inject.Inject; +import java.util.logging.Logger; + +/** + * + * @author baeldung + */ +public class SMSCommunicationMode implements CommunicationMode { + + @Inject + private Logger logger; + + @Override + public CommunicationModel getMode() { + return CommunicationModel.SMS; + } + + @Override + @MessageSentLoggable + public boolean sendMessage(String message) { + logger.info("SMS message sent"); + return true; + } + +} diff --git a/guice/src/main/java/com/baeldung/examples/guice/aop/MessageLogger.java b/guice/src/main/java/com/baeldung/examples/guice/aop/MessageLogger.java new file mode 100644 index 000000000000..379cd5f18b22 --- /dev/null +++ b/guice/src/main/java/com/baeldung/examples/guice/aop/MessageLogger.java @@ -0,0 +1,24 @@ + +package com.baeldung.examples.guice.aop; + +import com.google.inject.Inject; +import java.util.logging.Logger; +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; + +/** + * + * @author baeldung + */ +public class MessageLogger implements MethodInterceptor { + + @Override + public Object invoke(MethodInvocation invocation) throws Throwable { + Object[] objectArray = invocation.getArguments(); + int i = 0; + for (Object object : objectArray) { + Logger.getAnonymousLogger().info("Sending message: " + object.toString()); + } + return invocation.proceed(); + } +} diff --git a/guice/src/main/java/com/baeldung/examples/guice/aop/MessageSentLoggable.java b/guice/src/main/java/com/baeldung/examples/guice/aop/MessageSentLoggable.java new file mode 100644 index 000000000000..431c4bc0ced6 --- /dev/null +++ b/guice/src/main/java/com/baeldung/examples/guice/aop/MessageSentLoggable.java @@ -0,0 +1,17 @@ + +package com.baeldung.examples.guice.aop; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * @author baeldung + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface MessageSentLoggable { + +} diff --git a/guice/src/main/java/com/baeldung/examples/guice/binding/AOPModule.java b/guice/src/main/java/com/baeldung/examples/guice/binding/AOPModule.java new file mode 100644 index 000000000000..b41dcf16e571 --- /dev/null +++ b/guice/src/main/java/com/baeldung/examples/guice/binding/AOPModule.java @@ -0,0 +1,23 @@ + +package com.baeldung.examples.guice.binding; + +import com.baeldung.examples.guice.aop.MessageLogger; +import com.baeldung.examples.guice.aop.MessageSentLoggable; +import com.google.inject.AbstractModule; +import com.google.inject.matcher.Matchers; + +/** + * + * @author baeldung + */ +public class AOPModule extends AbstractModule { + + @Override + protected void configure() { + bindInterceptor(Matchers.any(), + Matchers.annotatedWith(MessageSentLoggable.class), + new MessageLogger() + ); + } + +} diff --git a/guice/src/main/java/com/baeldung/examples/guice/binding/BasicModule.java b/guice/src/main/java/com/baeldung/examples/guice/binding/BasicModule.java new file mode 100644 index 000000000000..1cd9d624ab50 --- /dev/null +++ b/guice/src/main/java/com/baeldung/examples/guice/binding/BasicModule.java @@ -0,0 +1,37 @@ + +package com.baeldung.examples.guice.binding; + +import com.baeldung.examples.guice.Communication; +import com.baeldung.examples.guice.CommunicationMode; +import com.baeldung.examples.guice.DefaultCommunicator; +import com.baeldung.examples.guice.EmailCommunicationMode; +import com.baeldung.examples.guice.IMCommunicationMode; +import com.baeldung.examples.guice.SMSCommunicationMode; +import com.google.inject.AbstractModule; +import com.google.inject.name.Names; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author baeldung + */ +public class BasicModule extends AbstractModule { + + @Override + protected void configure() { + try { + bind(Communication.class).toConstructor(Communication.class.getConstructor(Boolean.TYPE)); + } catch (NoSuchMethodException ex) { + Logger.getLogger(BasicModule.class.getName()).log(Level.SEVERE, null, ex); + } catch (SecurityException ex) { + Logger.getLogger(BasicModule.class.getName()).log(Level.SEVERE, null, ex); + } + bind(DefaultCommunicator.class).annotatedWith(Names.named("AnotherCommunicator")).to(DefaultCommunicator.class).asEagerSingleton(); + + bind(CommunicationMode.class).annotatedWith(Names.named("IMComms")).to(IMCommunicationMode.class); + bind(CommunicationMode.class).annotatedWith(Names.named("EmailComms")).to(EmailCommunicationMode.class); + bind(CommunicationMode.class).annotatedWith(Names.named("SMSComms")).to(SMSCommunicationMode.class); + } + +} diff --git a/guice/src/main/java/com/baeldung/examples/guice/constant/CommunicationModel.java b/guice/src/main/java/com/baeldung/examples/guice/constant/CommunicationModel.java new file mode 100644 index 000000000000..3483e9cefd72 --- /dev/null +++ b/guice/src/main/java/com/baeldung/examples/guice/constant/CommunicationModel.java @@ -0,0 +1,17 @@ + +package com.baeldung.examples.guice.constant; + +/** + * + * @author baeldung + */ +public enum CommunicationModel { + + EMAIL("Email"), SMS("SMS"), IM("IM"), PHONE("Phone"); + + final String name; + + CommunicationModel(String name) { + this.name = name; + } +} diff --git a/guice/src/main/java/com/baeldung/examples/guice/marker/Communicator.java b/guice/src/main/java/com/baeldung/examples/guice/marker/Communicator.java new file mode 100644 index 000000000000..45c729a9a376 --- /dev/null +++ b/guice/src/main/java/com/baeldung/examples/guice/marker/Communicator.java @@ -0,0 +1,14 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.baeldung.examples.guice.marker; + +/** + * + * @author Tayo + */ +public interface Communicator { + +} diff --git a/guice/src/main/java/com/baeldung/examples/guice/modules/BasicModule.java b/guice/src/main/java/com/baeldung/examples/guice/modules/BasicModule.java new file mode 100644 index 000000000000..f27d8b3a5398 --- /dev/null +++ b/guice/src/main/java/com/baeldung/examples/guice/modules/BasicModule.java @@ -0,0 +1,38 @@ + +package com.baeldung.examples.guice.modules; + +import com.baeldung.examples.guice.Communication; +import com.baeldung.examples.guice.CommunicationMode; +import com.baeldung.examples.guice.DefaultCommunicator; +import com.baeldung.examples.guice.EmailCommunicationMode; +import com.baeldung.examples.guice.IMCommunicationMode; +import com.baeldung.examples.guice.SMSCommunicationMode; +import com.google.inject.AbstractModule; +import com.google.inject.name.Names; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author baeldung + */ +public class BasicModule extends AbstractModule { + + @Override + protected void configure() { + try { + bind(Communication.class).toConstructor(Communication.class.getConstructor(Boolean.class)); + bind(Boolean.class).toInstance(true); + } catch (NoSuchMethodException ex) { + Logger.getLogger(com.baeldung.examples.guice.binding.BasicModule.class.getName()).log(Level.SEVERE, null, ex); + } catch (SecurityException ex) { + Logger.getLogger(com.baeldung.examples.guice.binding.BasicModule.class.getName()).log(Level.SEVERE, null, ex); + } + bind(DefaultCommunicator.class).annotatedWith(Names.named("AnotherCommunicator")).to(DefaultCommunicator.class).asEagerSingleton(); + + bind(CommunicationMode.class).annotatedWith(Names.named("IMComms")).to(IMCommunicationMode.class); + bind(CommunicationMode.class).annotatedWith(Names.named("EmailComms")).to(EmailCommunicationMode.class); + bind(CommunicationMode.class).annotatedWith(Names.named("SMSComms")).to(SMSCommunicationMode.class); + } + +} diff --git a/handling-spring-static-resources/pom.xml b/handling-spring-static-resources/pom.xml index 9a14ce937226..1e892ed23726 100644 --- a/handling-spring-static-resources/pom.xml +++ b/handling-spring-static-resources/pom.xml @@ -155,11 +155,6 @@ ${org.slf4j.version} - - org.slf4j - log4j-over-slf4j - ${org.slf4j.version} - diff --git a/handling-spring-static-resources/src/main/resources/logback.xml b/handling-spring-static-resources/src/main/resources/logback.xml index 1146dade632e..ec0dc2469ae0 100644 --- a/handling-spring-static-resources/src/main/resources/logback.xml +++ b/handling-spring-static-resources/src/main/resources/logback.xml @@ -1,20 +1,19 @@ + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - + + - - - - - - - - - + + + + + \ No newline at end of file diff --git a/hazelcast/src/main/resources/logback.xml b/hazelcast/src/main/resources/logback.xml index 8b566286b88d..ec0dc2469ae0 100644 --- a/hazelcast/src/main/resources/logback.xml +++ b/hazelcast/src/main/resources/logback.xml @@ -1,18 +1,19 @@ + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - - %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg %n - - + + - - - + + - - - - + + + \ No newline at end of file diff --git a/hbase/README.md b/hbase/README.md new file mode 100644 index 000000000000..df2683b27a17 --- /dev/null +++ b/hbase/README.md @@ -0,0 +1,3 @@ +### Relevant articles + +- [HBase with Java](http://www.baeldung.com/hbase) diff --git a/hbase/pom.xml b/hbase/pom.xml new file mode 100644 index 000000000000..c692054f0f88 --- /dev/null +++ b/hbase/pom.xml @@ -0,0 +1,58 @@ + + + + parent-modules + com.baeldung + 1.0.0-SNAPSHOT + + 4.0.0 + + hbase + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + + + + + org.apache.hbase + hbase-client + ${hbase.version} + + + commons-logging + commons-logging + + + + + org.apache.hbase + hbase + ${hbase.version} + pom + + + junit + junit + ${junit.version} + test + + + + + 1.3.0 + 4.12 + + + + \ No newline at end of file diff --git a/hbase/src/main/java/org/baeldung/hbase/HBaseClientOperations.java b/hbase/src/main/java/org/baeldung/hbase/HBaseClientOperations.java new file mode 100644 index 000000000000..c78eacc83447 --- /dev/null +++ b/hbase/src/main/java/org/baeldung/hbase/HBaseClientOperations.java @@ -0,0 +1,193 @@ +package org.baeldung.hbase; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.HColumnDescriptor; +import org.apache.hadoop.hbase.HTableDescriptor; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.client.*; +import org.apache.hadoop.hbase.filter.*; +import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp; +import org.apache.hadoop.hbase.filter.FilterList.Operator; +import org.apache.hadoop.hbase.util.Bytes; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +public class HBaseClientOperations { + private final static byte[] cellData = Bytes.toBytes("cell_data"); + + /** + * Drop tables if this value is set true. + */ + static boolean INITIALIZE_AT_FIRST = true; + + /** + * + * + * Row1Family1:Qualifier1Family1:Qualifier2 + * + * + * Row2Family1:Qualifier1Family2:Qualifier3 + * + * + * Row3Family1:Qualifier1Family2:Qualifier3 + * + *
+ */ + private final TableName table1 = TableName.valueOf("Table1"); + private final String family1 = "Family1"; + private final String family2 = "Family2"; + + private final byte[] row1 = Bytes.toBytes("Row1"); + private final byte[] row2 = Bytes.toBytes("Row2"); + private final byte[] row3 = Bytes.toBytes("Row3"); + private final byte[] qualifier1 = Bytes.toBytes("Qualifier1"); + private final byte[] qualifier2 = Bytes.toBytes("Qualifier2"); + private final byte[] qualifier3 = Bytes.toBytes("Qualifier3"); + + private void createTable(Admin admin) throws IOException { + HTableDescriptor desc = new HTableDescriptor(table1); + desc.addFamily(new HColumnDescriptor(family1)); + desc.addFamily(new HColumnDescriptor(family2)); + admin.createTable(desc); + } + + private void delete(Table table) throws IOException { + final byte[] rowToBeDeleted = Bytes.toBytes("RowToBeDeleted"); + System.out.println("\n*** DELETE ~Insert data and then delete it~ ***"); + + System.out.println("Inserting a data to be deleted later."); + Put put = new Put(rowToBeDeleted); + put.addColumn(family1.getBytes(), qualifier1, cellData); + table.put(put); + + Get get = new Get(rowToBeDeleted); + Result result = table.get(get); + byte[] value = result.getValue(family1.getBytes(), qualifier1); + System.out.println("Fetch the data: " + Bytes.toString(value)); + assert Arrays.equals(cellData, value); + + System.out.println("Deleting"); + Delete delete = new Delete(rowToBeDeleted); + delete.addColumn(family1.getBytes(), qualifier1); + table.delete(delete); + + result = table.get(get); + value = result.getValue(family1.getBytes(), qualifier1); + System.out.println("Fetch the data: " + Bytes.toString(value)); + assert Arrays.equals(null, value); + + System.out.println("Done. "); + } + + private void deleteTable(Admin admin) throws IOException { + if (admin.tableExists(table1)) { + admin.disableTable(table1); + admin.deleteTable(table1); + } + } + + private void filters(Table table) throws IOException { + System.out.println("\n*** FILTERS ~ scanning with filters to fetch a row of which key is larget than \"Row1\"~ ***"); + Filter filter1 = new PrefixFilter(row1); + Filter filter2 = new QualifierFilter(CompareOp.GREATER_OR_EQUAL, new BinaryComparator( + qualifier1)); + + List filters = Arrays.asList(filter1, filter2); + + Scan scan = new Scan(); + scan.setFilter(new FilterList(Operator.MUST_PASS_ALL, filters)); + + try (ResultScanner scanner = table.getScanner(scan)) { + int i = 0; + for (Result result : scanner) { + System.out.println("Filter " + scan.getFilter() + " matched row: " + result); + i++; + } + assert i == 2 : "This filtering sample should return 1 row but was " + i + "."; + } + System.out.println("Done. "); + } + + private void get(Table table) throws IOException { + System.out.println("\n*** GET example ~fetching the data in Family1:Qualifier1~ ***"); + + Get g = new Get(row1); + Result r = table.get(g); + byte[] value = r.getValue(family1.getBytes(), qualifier1); + + System.out.println("Fetched value: " + Bytes.toString(value)); + assert Arrays.equals(cellData, value); + System.out.println("Done. "); + } + + private void put(Admin admin, Table table) throws IOException { + System.out.println("\n*** PUT example ~inserting \"cell-data\" into Family1:Qualifier1 of Table1 ~ ***"); + + // Row1 => Family1:Qualifier1, Family1:Qualifier2 + Put p = new Put(row1); + p.addImmutable(family1.getBytes(), qualifier1, cellData); + p.addImmutable(family1.getBytes(), qualifier2, cellData); + table.put(p); + + // Row2 => Family1:Qualifier1, Family2:Qualifier3 + p = new Put(row2); + p.addImmutable(family1.getBytes(), qualifier1, cellData); + p.addImmutable(family2.getBytes(), qualifier3, cellData); + table.put(p); + + // Row3 => Family1:Qualifier1, Family2:Qualifier3 + p = new Put(row3); + p.addImmutable(family1.getBytes(), qualifier1, cellData); + p.addImmutable(family2.getBytes(), qualifier3, cellData); + table.put(p); + + admin.disableTable(table1); + try { + HColumnDescriptor desc = new HColumnDescriptor(row1); + admin.addColumn(table1, desc); + System.out.println("Success."); + } catch (Exception e) { + System.out.println("Failed."); + System.out.println(e.getMessage()); + } finally { + admin.enableTable(table1); + } + System.out.println("Done. "); + } + + public void run(Configuration config) throws IOException { + try (Connection connection = ConnectionFactory.createConnection(config)) { + + Admin admin = connection.getAdmin(); + if (INITIALIZE_AT_FIRST) { + deleteTable(admin); + } + + if (!admin.tableExists(table1)) { + createTable(admin); + } + + Table table = connection.getTable(table1); + put(admin, table); + get(table); + scan(table); + filters(table); + delete(table); + } + } + + private void scan(Table table) throws IOException { + System.out.println("\n*** SCAN example ~fetching data in Family1:Qualifier1 ~ ***"); + + Scan scan = new Scan(); + scan.addColumn(family1.getBytes(), qualifier1); + + try (ResultScanner scanner = table.getScanner(scan)) { + for (Result result : scanner) + System.out.println("Found row: " + result); + } + System.out.println("Done."); + } +} \ No newline at end of file diff --git a/hbase/src/main/java/org/baeldung/hbase/HbaseClientExample.java b/hbase/src/main/java/org/baeldung/hbase/HbaseClientExample.java new file mode 100644 index 000000000000..07cb7df48077 --- /dev/null +++ b/hbase/src/main/java/org/baeldung/hbase/HbaseClientExample.java @@ -0,0 +1,38 @@ +package org.baeldung.hbase; + + +import com.google.protobuf.ServiceException; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.hbase.HBaseConfiguration; +import org.apache.hadoop.hbase.MasterNotRunningException; +import org.apache.hadoop.hbase.client.HBaseAdmin; + +import java.io.IOException; + +//install hbase locally & hbase master start +public class HbaseClientExample { + + public static void main(String[] args) throws IOException, ServiceException { + new HbaseClientExample().connect(); + } + + private void connect() throws IOException, ServiceException { + Configuration config = HBaseConfiguration.create(); + + String path = this.getClass().getClassLoader().getResource("hbase-site.xml").getPath(); + + config.addResource(new Path(path)); + + try { + HBaseAdmin.checkHBaseAvailable(config); + } catch (MasterNotRunningException e) { + System.out.println("HBase is not running." + e.getMessage()); + return; + } + + HBaseClientOperations HBaseClientOperations = new HBaseClientOperations(); + HBaseClientOperations.run(config); + } + +} \ No newline at end of file diff --git a/hbase/src/main/resources/hbase-site.xml b/hbase/src/main/resources/hbase-site.xml new file mode 100644 index 000000000000..895529161cce --- /dev/null +++ b/hbase/src/main/resources/hbase-site.xml @@ -0,0 +1,12 @@ + + + + + hbase.zookeeper.quorum + localhost + + + hbase.zookeeper.property.clientPort + 2181 + + \ No newline at end of file diff --git a/hibernate5/pom.xml b/hibernate5/pom.xml new file mode 100644 index 000000000000..b7473ec5b719 --- /dev/null +++ b/hibernate5/pom.xml @@ -0,0 +1,62 @@ + + + 4.0.0 + + com.baeldung + parent-modules + 1.0.0-SNAPSHOT + + com.baeldung + hibernate5 + 0.0.1-SNAPSHOT + hibernate5 + http://maven.apache.org + + UTF-8 + + 3.6.0 + + + + org.hibernate + hibernate-core + 5.2.9.Final + + + junit + junit + 4.12 + + + com.h2database + h2 + 1.4.194 + + + + hibernate5 + + + src/main/resources + true + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + 1.8 + 1.8 + + + + + + + + diff --git a/hibernate5/src/main/java/com/baeldung/hibernate/App.java b/hibernate5/src/main/java/com/baeldung/hibernate/App.java new file mode 100644 index 000000000000..26a40bb782e3 --- /dev/null +++ b/hibernate5/src/main/java/com/baeldung/hibernate/App.java @@ -0,0 +1,30 @@ +package com.baeldung.hibernate; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.Transaction; + +import com.baeldung.hibernate.pojo.Supplier; + +/** + * Hello world! + * + */ +public class App { + public static void main(String[] args) { + try { + // NOTE: this is just for boostrap testing for multitenancy. + System.out.println("Checking the system."); + SessionFactory sessionFactory = HibernateMultiTenantUtil.getSessionFactory(); + Session currentSession = sessionFactory.withOptions().tenantIdentifier("h2db1").openSession(); + Transaction transaction = currentSession.getTransaction(); + transaction.begin(); + currentSession.createCriteria(Supplier.class).list().stream().forEach(System.out::println); + transaction.commit(); + + } catch (Exception e) { + e.printStackTrace(); + } + + } +} diff --git a/hibernate5/src/main/java/com/baeldung/hibernate/ConfigurableMultiTenantConnectionProvider.java b/hibernate5/src/main/java/com/baeldung/hibernate/ConfigurableMultiTenantConnectionProvider.java new file mode 100644 index 000000000000..b9ed2bd13906 --- /dev/null +++ b/hibernate5/src/main/java/com/baeldung/hibernate/ConfigurableMultiTenantConnectionProvider.java @@ -0,0 +1,41 @@ +package com.baeldung.hibernate; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; + +import org.hibernate.engine.jdbc.connections.spi.AbstractMultiTenantConnectionProvider; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; + +public class ConfigurableMultiTenantConnectionProvider extends AbstractMultiTenantConnectionProvider { + + private final Map connectionProviderMap = + new HashMap<>(); + + + public ConfigurableMultiTenantConnectionProvider( + Map connectionProviderMap) { + this.connectionProviderMap.putAll( connectionProviderMap ); + } + @Override + protected ConnectionProvider getAnyConnectionProvider() { + System.out.println("Any"); + return connectionProviderMap.values().iterator().next(); + } + + @Override + protected ConnectionProvider selectConnectionProvider(String tenantIdentifier) { + System.out.println("Specific"); + return connectionProviderMap.get( tenantIdentifier ); + } + + @Override + public Connection getConnection(String tenantIdentifier) throws SQLException { + Connection connection = super.getConnection(tenantIdentifier); + // uncomment to see option 2 for SCHEMA strategy. + //connection.createStatement().execute("SET SCHEMA '" + tenantIdentifier + "'"); + return connection; + } + +} \ No newline at end of file diff --git a/hibernate5/src/main/java/com/baeldung/hibernate/HibernateMultiTenantUtil.java b/hibernate5/src/main/java/com/baeldung/hibernate/HibernateMultiTenantUtil.java new file mode 100644 index 000000000000..c3e7b621d0b1 --- /dev/null +++ b/hibernate5/src/main/java/com/baeldung/hibernate/HibernateMultiTenantUtil.java @@ -0,0 +1,95 @@ +package com.baeldung.hibernate; + +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import org.hibernate.SessionFactory; +import org.hibernate.boot.Metadata; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.Configuration; +import org.hibernate.cfg.Environment; +import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.service.ServiceRegistry; + +import com.baeldung.hibernate.pojo.Supplier; + +public class HibernateMultiTenantUtil { + private static SessionFactory sessionFactory; + private static Map connectionProviderMap = new HashMap<>(); + private static final String[] tenantDBNames = { "mydb1","mydb2"}; + + public static SessionFactory getSessionFactory() throws UnsupportedTenancyException { + if (sessionFactory == null) { + Configuration configuration = new Configuration().configure(); + ServiceRegistry serviceRegistry = configureServiceRegistry(configuration); + sessionFactory = makeSessionFactory (serviceRegistry); +// sessionFactory = configuration.buildSessionFactory(serviceRegistry); + + + } + return sessionFactory; + } + + private static SessionFactory makeSessionFactory(ServiceRegistry serviceRegistry) { + MetadataSources metadataSources = new MetadataSources( serviceRegistry ); + for(Class annotatedClasses : getAnnotatedClasses()) { + metadataSources.addAnnotatedClass( annotatedClasses ); + } + + Metadata metadata = metadataSources.buildMetadata(); + return metadata.getSessionFactoryBuilder().build(); + + } + + private static Class[] getAnnotatedClasses() { + return new Class[] { + Supplier.class + }; + } + + private static ServiceRegistry configureServiceRegistry(Configuration configuration) throws UnsupportedTenancyException { + Properties properties = configuration.getProperties(); + + connectionProviderMap = setUpConnectionProviders(properties, tenantDBNames); + properties.put(AvailableSettings.MULTI_TENANT_CONNECTION_PROVIDER, new ConfigurableMultiTenantConnectionProvider(connectionProviderMap)); + + return new StandardServiceRegistryBuilder().applySettings(properties).build(); + } + + private static Map setUpConnectionProviders(Properties properties, String[] tenantNames) throws UnsupportedTenancyException { + Map providerMap = new HashMap<>(); + for (String tenant : tenantNames) { + DriverManagerConnectionProviderImpl connectionProvider = new DriverManagerConnectionProviderImpl(); + + String tenantStrategy = properties.getProperty("hibernate.multiTenancy"); + System.out.println("Strategy:"+tenantStrategy); + properties.put(Environment.URL, tenantUrl(properties.getProperty(Environment.URL), tenant, tenantStrategy)); + System.out.println("URL:"+properties.getProperty(Environment.URL)); + connectionProvider.configure(properties); + System.out.println("Tenant:"+tenant); + providerMap.put(tenant, connectionProvider); + + } + System.out.println("Added connections for:"); + providerMap.keySet().stream().forEach(System.out::println); + return providerMap; + } + + private static Object tenantUrl(String originalUrl, String tenant, String tenantStrategy) throws UnsupportedTenancyException { + if (tenantStrategy.toUpperCase().equals("DATABASE")) { + return originalUrl.replace(DEFAULT_DB_NAME, tenant); + } else if (tenantStrategy.toUpperCase().equals("SCHEMA")) { + return originalUrl + String.format(SCHEMA_TOKEN, tenant); + } else { + throw new UnsupportedTenancyException("Not yet supported"); + } + } + + public static final String SCHEMA_TOKEN = ";INIT=CREATE SCHEMA IF NOT EXISTS %1$s\\;SET SCHEMA %1$s"; + public static final String DEFAULT_DB_NAME = "mydb1"; + +} diff --git a/hibernate5/src/main/java/com/baeldung/hibernate/HibernateUtil.java b/hibernate5/src/main/java/com/baeldung/hibernate/HibernateUtil.java new file mode 100644 index 000000000000..c1f7301d46ad --- /dev/null +++ b/hibernate5/src/main/java/com/baeldung/hibernate/HibernateUtil.java @@ -0,0 +1,24 @@ +package com.baeldung.hibernate; + +import org.hibernate.SessionFactory; +import org.hibernate.cfg.Configuration; + +public class HibernateUtil { + + private static final SessionFactory sessionFactory; + + static { + try { + Configuration configuration = new Configuration().configure(); + sessionFactory = configuration.buildSessionFactory(); + + } catch (Throwable ex) { + System.err.println("Initial SessionFactory creation failed." + ex); + throw new ExceptionInInitializerError(ex); + } + } + + public static SessionFactory getSessionFactory() { + return sessionFactory; + } +} \ No newline at end of file diff --git a/hibernate5/src/main/java/com/baeldung/hibernate/UnsupportedTenancyException.java b/hibernate5/src/main/java/com/baeldung/hibernate/UnsupportedTenancyException.java new file mode 100644 index 000000000000..99d9505ea3a3 --- /dev/null +++ b/hibernate5/src/main/java/com/baeldung/hibernate/UnsupportedTenancyException.java @@ -0,0 +1,8 @@ +package com.baeldung.hibernate; + +public class UnsupportedTenancyException extends Exception { + public UnsupportedTenancyException (String message) { + super(message); + } + +} diff --git a/hibernate5/src/main/java/com/baeldung/hibernate/pojo/Supplier.java b/hibernate5/src/main/java/com/baeldung/hibernate/pojo/Supplier.java new file mode 100644 index 000000000000..d0187bba4751 --- /dev/null +++ b/hibernate5/src/main/java/com/baeldung/hibernate/pojo/Supplier.java @@ -0,0 +1,67 @@ +package com.baeldung.hibernate.pojo; +// Generated Feb 9, 2017 11:31:36 AM by Hibernate Tools 5.1.0.Final + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.junit.runners.Suite.SuiteClasses; + + +/** + * Suppliers generated by hbm2java + */ +@Entity(name = "Supplier") +@Table(name ="Supplier") +public class Supplier implements java.io.Serializable { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + private String name; + private String country; + + public Supplier() { + } + + public Supplier(String name, String country) { + this.name = name; + this.country = country; + } + + public Integer getId() { + return this.id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCountry() { + return this.country; + } + + public void setCountry(String country) { + this.country = country; + } + + @Override + public String toString() { + return new StringBuffer().append("[").append(id).append(",").append(name).append(",").append(country).append("]").toString(); + } + + @Override + public boolean equals(Object obj) { + return name.equals(((Supplier) obj).getName()); + } +} diff --git a/hibernate5/src/main/java/hibernate.cfg.xml b/hibernate5/src/main/java/hibernate.cfg.xml new file mode 100644 index 000000000000..26be05f93189 --- /dev/null +++ b/hibernate5/src/main/java/hibernate.cfg.xml @@ -0,0 +1,14 @@ + + + + + org.h2.Driver + jdbc:h2:mem:mydb1;DB_CLOSE_DELAY=-1 + sa + + org.hibernate.dialect.H2Dialect + DATABASE + + diff --git a/hibernate5/src/test/java/com/baeldung/hibernate/MultiTenantHibernateTest.java b/hibernate5/src/test/java/com/baeldung/hibernate/MultiTenantHibernateTest.java new file mode 100644 index 000000000000..4a701de48d98 --- /dev/null +++ b/hibernate5/src/test/java/com/baeldung/hibernate/MultiTenantHibernateTest.java @@ -0,0 +1,73 @@ +package com.baeldung.hibernate; +import static org.junit.Assert.assertNotEquals; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.Transaction; +import org.junit.Before; +import org.junit.Test; + +import com.baeldung.hibernate.pojo.Supplier; + + +public class MultiTenantHibernateTest { + @Test + public void givenDBMode_whenFetchingSuppliers_thenComparingFromDbs () { + SessionFactory sessionFactory; + try { + sessionFactory = HibernateMultiTenantUtil.getSessionFactory(); + + Session db1Session = sessionFactory + .withOptions().tenantIdentifier("mydb1").openSession(); + + initDb1(db1Session); + + Transaction transaction = db1Session.getTransaction(); + transaction.begin(); + Supplier supplierFromDB1 = (Supplier)db1Session.createCriteria(Supplier.class).list().get(0); + transaction.commit(); + + Session db2Session = sessionFactory + .withOptions().tenantIdentifier("mydb2").openSession(); + + initDb2(db2Session); + db2Session.getTransaction().begin(); + Supplier supplierFromDB2 = (Supplier) db2Session.createCriteria(Supplier.class).list().get(0); + db2Session.getTransaction().commit(); + + System.out.println(supplierFromDB1); + System.out.println(supplierFromDB2); + + assertNotEquals(supplierFromDB1, supplierFromDB2); + } catch (UnsupportedTenancyException e) { + e.printStackTrace(); + } + } + + + + private void initDb1(Session db1Session) { + System.out.println("Init DB1"); + Transaction transaction = db1Session.getTransaction(); + transaction.begin(); + db1Session.createSQLQuery("DROP ALL OBJECTS").executeUpdate(); + db1Session.createSQLQuery("create table Supplier (id integer generated by default as identity, country varchar(255), name varchar(255), primary key (id))").executeUpdate(); + db1Session.createSQLQuery("insert into Supplier (id, country, name) values (null, 'John', 'USA')").executeUpdate(); + transaction.commit(); + } + + private void initDb2(Session db2Session) { + System.out.println("Init DB2"); + Transaction transaction = db2Session.getTransaction(); + transaction.begin(); + db2Session.createSQLQuery("DROP ALL OBJECTS").executeUpdate(); + db2Session.createSQLQuery("create table Supplier (id integer generated by default as identity, country varchar(255), name varchar(255), primary key (id))").executeUpdate(); + db2Session.createSQLQuery("insert into Supplier (id, country, name) values (null, 'Miller', 'UK')").executeUpdate(); + transaction.commit(); + } +} diff --git a/httpclient/pom.xml b/httpclient/pom.xml index eec705b2248e..976f04c9c249 100644 --- a/httpclient/pom.xml +++ b/httpclient/pom.xml @@ -41,6 +41,12 @@ org.apache.httpcomponents fluent-hc ${httpclient.version} + + + commons-logging + commons-logging + + @@ -59,6 +65,12 @@ org.apache.httpcomponents httpasyncclient ${httpasyncclient.version} + + + commons-logging + commons-logging + + @@ -80,11 +92,6 @@ ${org.slf4j.version} - - org.slf4j - log4j-over-slf4j - ${org.slf4j.version} - diff --git a/httpclient/src/main/resources/logback.xml b/httpclient/src/main/resources/logback.xml index aa1e9cd52209..ec0dc2469ae0 100644 --- a/httpclient/src/main/resources/logback.xml +++ b/httpclient/src/main/resources/logback.xml @@ -1,5 +1,5 @@ + - web - %date [%thread] %-5level %logger{36} - %message%n @@ -7,11 +7,13 @@ - - + + + + + - \ No newline at end of file diff --git a/hystrix/pom.xml b/hystrix/pom.xml index ba1a59663150..5ccc66709f9a 100644 --- a/hystrix/pom.xml +++ b/hystrix/pom.xml @@ -56,6 +56,12 @@ com.netflix.hystrix hystrix-core ${hystrix-core.version} + + + commons-logging + commons-logging + + com.netflix.hystrix diff --git a/image-processing/pom.xml b/image-processing/pom.xml index 8766da571e55..1e19686be25a 100644 --- a/image-processing/pom.xml +++ b/image-processing/pom.xml @@ -17,11 +17,23 @@ net.imagej ij ${ij.version} + + + commons-logging + commons-logging + + org.openimaj core-image ${core-image.version} + + + commons-logging + commons-logging + + diff --git a/jackson/README.md b/jackson/README.md index d9faa377f1ee..51919f67697a 100644 --- a/jackson/README.md +++ b/jackson/README.md @@ -26,3 +26,5 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring - [Inheritance with Jackson](http://www.baeldung.com/jackson-inheritance) - [Guide to @JsonFormat in Jackson](http://www.baeldung.com/jackson-jsonformat) - [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) diff --git a/jackson/pom.xml b/jackson/pom.xml index 881ba8e24c4e..df2f65d3594c 100644 --- a/jackson/pom.xml +++ b/jackson/pom.xml @@ -154,11 +154,6 @@ ${org.slf4j.version} - - org.slf4j - log4j-over-slf4j - ${org.slf4j.version} - @@ -195,7 +190,7 @@ - 2.8.6 + 2.8.7 1.7.21 diff --git a/jackson/src/main/java/com/baeldung/jackson/entities/ClassWithAMap.java b/jackson/src/main/java/com/baeldung/jackson/entities/ClassWithAMap.java new file mode 100644 index 000000000000..54ebff8a56f1 --- /dev/null +++ b/jackson/src/main/java/com/baeldung/jackson/entities/ClassWithAMap.java @@ -0,0 +1,24 @@ +package com.baeldung.jackson.entities; + +import java.util.Map; + +import com.baeldung.jackson.serialization.MyPairDeserializer; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; + +public class ClassWithAMap { + + @JsonProperty("map") + @JsonDeserialize(keyUsing = MyPairDeserializer.class) + private final Map map; + + @JsonCreator + public ClassWithAMap(Map map) { + this.map = map; + } + + public Map getMap() { + return map; + } +} \ No newline at end of file diff --git a/jackson/src/main/java/com/baeldung/jackson/entities/MyPair.java b/jackson/src/main/java/com/baeldung/jackson/entities/MyPair.java new file mode 100644 index 000000000000..ebe41890fec7 --- /dev/null +++ b/jackson/src/main/java/com/baeldung/jackson/entities/MyPair.java @@ -0,0 +1,80 @@ +package com.baeldung.jackson.entities; + +import com.fasterxml.jackson.annotation.JsonValue; + +public class MyPair { + + private String first; + private String second; + + public MyPair(String first, String second) { + this.first = first; + this.second = second; + } + + public MyPair(String both) { + String[] pairs = both.split("and"); + this.first = pairs[0].trim(); + this.second = pairs[1].trim(); + } + + @Override + @JsonValue + public String toString() { + return first + " and " + second; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((first == null) ? 0 : first.hashCode()); + result = prime * result + ((second == null) ? 0 : second.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof MyPair)) { + return false; + } + MyPair other = (MyPair) obj; + if (first == null) { + if (other.first != null) { + return false; + } + } else if (!first.equals(other.first)) { + return false; + } + if (second == null) { + if (other.second != null) { + return false; + } + } else if (!second.equals(other.second)) { + return false; + } + return true; + } + + public String getFirst() { + return first; + } + + public void setFirst(String first) { + this.first = first; + } + + public String getSecond() { + return second; + } + + public void setSecond(String second) { + this.second = second; + } +} \ No newline at end of file diff --git a/jackson/src/main/java/com/baeldung/jackson/serialization/MyPairDeserializer.java b/jackson/src/main/java/com/baeldung/jackson/serialization/MyPairDeserializer.java new file mode 100644 index 000000000000..0aa6db98d0ca --- /dev/null +++ b/jackson/src/main/java/com/baeldung/jackson/serialization/MyPairDeserializer.java @@ -0,0 +1,18 @@ +package com.baeldung.jackson.serialization; + +import java.io.IOException; + +import com.baeldung.jackson.entities.MyPair; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.KeyDeserializer; + +public class MyPairDeserializer extends KeyDeserializer { + + @Override + public MyPair deserializeKey(String key, DeserializationContext ctxt) + throws IOException, JsonProcessingException { + + return new MyPair(key); + } +} \ No newline at end of file diff --git a/jackson/src/main/java/com/baeldung/jackson/serialization/MyPairSerializer.java b/jackson/src/main/java/com/baeldung/jackson/serialization/MyPairSerializer.java new file mode 100644 index 000000000000..68afb6c19315 --- /dev/null +++ b/jackson/src/main/java/com/baeldung/jackson/serialization/MyPairSerializer.java @@ -0,0 +1,25 @@ +package com.baeldung.jackson.serialization; + +import java.io.IOException; +import java.io.StringWriter; + +import com.baeldung.jackson.entities.MyPair; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializerProvider; + +public class MyPairSerializer extends JsonSerializer { + + private final ObjectMapper mapper = new ObjectMapper(); + + @Override + public void serialize(MyPair value, JsonGenerator gen, + SerializerProvider serializers) throws IOException, + JsonProcessingException { + StringWriter writer = new StringWriter(); + mapper.writeValue(writer, value); + gen.writeFieldName(writer.toString()); + } +} \ No newline at end of file diff --git a/jackson/src/main/resources/logback.xml b/jackson/src/main/resources/logback.xml index 1146dade632e..ec0dc2469ae0 100644 --- a/jackson/src/main/resources/logback.xml +++ b/jackson/src/main/resources/logback.xml @@ -1,20 +1,19 @@ + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - + + - - - - - - - - - + + + + + \ No newline at end of file diff --git a/jackson/src/test/java/com/baeldung/jackson/deserialization/JacksonMapDeserializeTest.java b/jackson/src/test/java/com/baeldung/jackson/deserialization/JacksonMapDeserializeTest.java new file mode 100644 index 000000000000..5021abaac105 --- /dev/null +++ b/jackson/src/test/java/com/baeldung/jackson/deserialization/JacksonMapDeserializeTest.java @@ -0,0 +1,69 @@ +package com.baeldung.jackson.deserialization; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.junit.Assert; +import org.junit.Test; + +import com.baeldung.jackson.entities.ClassWithAMap; +import com.baeldung.jackson.entities.MyPair; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class JacksonMapDeserializeTest { + + private Map map; + private Map cmap; + final ObjectMapper mapper = new ObjectMapper(); + + @Test + public void whenSimpleMapDeserialize_thenCorrect() + throws JsonParseException, JsonMappingException, IOException { + + final String jsonInput = "{\"key\": \"value\"}"; + TypeReference> typeRef = new TypeReference>() { + }; + + final Map map = mapper.readValue(jsonInput, typeRef); + + Assert.assertEquals("value", map.get("key")); + } + + @Test + public void whenObjectStringMapDeserialize_thenCorrect() + throws JsonParseException, JsonMappingException, IOException { + + final String jsonInput = "{\"Abbott and Costello\":\"Comedy\"}"; + + TypeReference> typeRef = new TypeReference>() { + }; + + map = mapper.readValue(jsonInput, typeRef); + + Assert.assertEquals("Comedy", map.get(new MyPair("Abbott", "Costello"))); + + ClassWithAMap classWithMap = mapper.readValue(jsonInput, + ClassWithAMap.class); + + Assert.assertEquals("Comedy", + classWithMap.getMap().get(new MyPair("Abbott", "Costello"))); + } + + @Test + public void whenObjectObjectMapDeserialize_thenCorrect() + throws JsonParseException, JsonMappingException, IOException { + + final String jsonInput = "{\"Abbott and Costello\" : \"Comedy and 1940s\"}"; + TypeReference> typeRef = new TypeReference>() { + }; + + cmap = mapper.readValue(jsonInput, typeRef); + + Assert.assertEquals(new MyPair("Comedy", "1940s"), + cmap.get(new MyPair("Abbott", "Costello"))); + } +} diff --git a/jackson/src/test/java/com/baeldung/jackson/serialization/JacksonMapSerializeTest.java b/jackson/src/test/java/com/baeldung/jackson/serialization/JacksonMapSerializeTest.java new file mode 100644 index 000000000000..a5ee76f3b88d --- /dev/null +++ b/jackson/src/test/java/com/baeldung/jackson/serialization/JacksonMapSerializeTest.java @@ -0,0 +1,70 @@ +package com.baeldung.jackson.serialization; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Assert; +import org.junit.Test; + +import com.baeldung.jackson.entities.MyPair; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.MapSerializer; + +public class JacksonMapSerializeTest { + + @JsonSerialize(keyUsing = MyPairSerializer.class) + private Map map; + + @JsonSerialize(keyUsing = MapSerializer.class) + private Map cmap; + + @JsonSerialize(keyUsing = MyPairSerializer.class) + private MyPair mapKey; + + @JsonSerialize(keyUsing = MyPairSerializer.class) + private MyPair mapValue; + + final ObjectMapper mapper = new ObjectMapper(); + + @Test + public void whenSimpleMapSerialize_thenCorrect() + throws JsonProcessingException { + + Map map = new HashMap<>(); + map.put("key", "value"); + + final String jsonResult = mapper.writeValueAsString(map); + + Assert.assertEquals("{\"key\":\"value\"}", jsonResult); + } + + @Test + public void whenCustomObjectStringMapSerialize_thenCorrect() + throws JsonProcessingException { + + map = new HashMap<>(); + MyPair key = new MyPair("Abbott", "Costello"); + map.put(key, "Comedy"); + + final String jsonResult = mapper.writeValueAsString(map); + + Assert.assertEquals("{\"Abbott and Costello\":\"Comedy\"}", jsonResult); + } + + @Test + public void whenCustomObjectObjectMapSerialize_thenCorrect() + throws JsonProcessingException { + + cmap = new HashMap<>(); + mapKey = new MyPair("Abbott", "Costello"); + mapValue = new MyPair("Comedy", "1940's"); + cmap.put(mapKey, mapValue); + + final String jsonResult = mapper.writeValueAsString(cmap); + + Assert.assertEquals("{\"Abbott and Costello\":\"Comedy and 1940's\"}", + jsonResult); + } +} diff --git a/jackson/src/test/java/com/baeldung/jackson/streaming/JacksonStreamingAPITest.java b/jackson/src/test/java/com/baeldung/jackson/streaming/JacksonStreamingAPITest.java new file mode 100644 index 000000000000..6f617933159c --- /dev/null +++ b/jackson/src/test/java/com/baeldung/jackson/streaming/JacksonStreamingAPITest.java @@ -0,0 +1,119 @@ +package com.baeldung.jackson.streaming; + + +import com.fasterxml.jackson.core.*; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +import static junit.framework.Assert.assertNull; +import static junit.framework.Assert.assertTrue; +import static junit.framework.TestCase.assertEquals; + +public class JacksonStreamingAPITest { + + @Test + public void givenJsonGenerator_whenAppendJsonToIt_thenGenerateJson() throws IOException { + //given + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + JsonFactory jfactory = new JsonFactory(); + JsonGenerator jGenerator = jfactory.createGenerator(stream, JsonEncoding.UTF8); + + //when + jGenerator.writeStartObject(); + jGenerator.writeStringField("name", "Tom"); + jGenerator.writeNumberField("age", 25); + jGenerator.writeFieldName("address"); + jGenerator.writeStartArray(); + jGenerator.writeString("Poland"); + jGenerator.writeString("5th avenue"); + jGenerator.writeEndArray(); + jGenerator.writeEndObject(); + jGenerator.close(); + + //then + String json = new String(stream.toByteArray(), "UTF-8"); + assertEquals(json, "{\"name\":\"Tom\",\"age\":25,\"address\":[\"Poland\",\"5th avenue\"]}"); + } + + @Test + public void givenJson_whenReadItUsingStreamAPI_thenShouldCreateProperJsonObject() throws IOException { + //given + String json = "{\"name\":\"Tom\",\"age\":25,\"address\":[\"Poland\",\"5th avenue\"]}"; + JsonFactory jfactory = new JsonFactory(); + JsonParser jParser = jfactory.createParser(json); + + String parsedName = null; + Integer parsedAge = null; + List addresses = new LinkedList<>(); + + //when + while (jParser.nextToken() != JsonToken.END_OBJECT) { + + String fieldname = jParser.getCurrentName(); + if ("name".equals(fieldname)) { + jParser.nextToken(); + parsedName = jParser.getText(); + + } + + if ("age".equals(fieldname)) { + jParser.nextToken(); + parsedAge = jParser.getIntValue(); + + } + + if ("address".equals(fieldname)) { + jParser.nextToken(); + + while (jParser.nextToken() != JsonToken.END_ARRAY) { + addresses.add(jParser.getText()); + } + } + + } + jParser.close(); + + //then + assertEquals(parsedName, "Tom"); + assertEquals(parsedAge, (Integer) 25); + assertEquals(addresses, Arrays.asList("Poland", "5th avenue")); + + } + + @Test + public void givenJson_whenWantToExtractPartOfIt_thenShouldExtractOnlyNeededFieldWithoutGoingThroughWholeJSON() throws IOException { + //given + String json = "{\"name\":\"Tom\",\"age\":25,\"address\":[\"Poland\",\"5th avenue\"]}"; + JsonFactory jfactory = new JsonFactory(); + JsonParser jParser = jfactory.createParser(json); + + String parsedName = null; + Integer parsedAge = null; + List addresses = new LinkedList<>(); + + //when + while (jParser.nextToken() != JsonToken.END_OBJECT) { + + String fieldname = jParser.getCurrentName(); + + if ("age".equals(fieldname)) { + jParser.nextToken(); + parsedAge = jParser.getIntValue(); + return; + } + + } + jParser.close(); + + //then + assertNull(parsedName); + assertEquals(parsedAge, (Integer) 25); + assertTrue(addresses.isEmpty()); + + } +} diff --git a/java-cassandra/pom.xml b/java-cassandra/pom.xml index 5796043b95c2..dfc54526176c 100644 --- a/java-cassandra/pom.xml +++ b/java-cassandra/pom.xml @@ -49,11 +49,6 @@ ${org.slf4j.version} - - org.slf4j - log4j-over-slf4j - ${org.slf4j.version} - junit diff --git a/java-websocket/README.md b/java-websocket/README.md new file mode 100644 index 000000000000..f9f078404310 --- /dev/null +++ b/java-websocket/README.md @@ -0,0 +1,3 @@ +### Relevant articles + +- [A Guide to the Java API for WebSocket](http://www.baeldung.com/java-websockets) diff --git a/java-websocket/pom.xml b/java-websocket/pom.xml new file mode 100644 index 000000000000..e19706b4d93d --- /dev/null +++ b/java-websocket/pom.xml @@ -0,0 +1,42 @@ + + 4.0.0 + com.baeldung + java-websocket + war + 0.0.1-SNAPSHOT + java-websocket Maven Webapp + http://maven.apache.org + + + UTF-8 + + + + + javax.websocket + javax.websocket-api + 1.1 + provided + + + com.google.code.gson + gson + 2.8.0 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.5.1 + + 1.8 + 1.8 + + + + + diff --git a/java-websocket/src/main/java/com/baeldung/model/Message.java b/java-websocket/src/main/java/com/baeldung/model/Message.java new file mode 100644 index 000000000000..3d5bbcbc5dc1 --- /dev/null +++ b/java-websocket/src/main/java/com/baeldung/model/Message.java @@ -0,0 +1,36 @@ +package com.baeldung.model; + +public class Message { + private String from; + private String to; + private String content; + + @Override + public String toString() { + return super.toString(); + } + + public String getFrom() { + return from; + } + + public void setFrom(String from) { + this.from = from; + } + + public String getTo() { + return to; + } + + public void setTo(String to) { + this.to = to; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } +} diff --git a/java-websocket/src/main/java/com/baeldung/websocket/ChatEndpoint.java b/java-websocket/src/main/java/com/baeldung/websocket/ChatEndpoint.java new file mode 100644 index 000000000000..c4e20f4b27e0 --- /dev/null +++ b/java-websocket/src/main/java/com/baeldung/websocket/ChatEndpoint.java @@ -0,0 +1,71 @@ +package com.baeldung.websocket; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; + +import javax.websocket.EncodeException; +import javax.websocket.OnClose; +import javax.websocket.OnError; +import javax.websocket.OnMessage; +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; + +import com.baeldung.model.Message; + +@ServerEndpoint(value = "/chat/{username}", decoders = MessageDecoder.class, encoders = MessageEncoder.class) +public class ChatEndpoint { + private Session session; + private static final Set chatEndpoints = new CopyOnWriteArraySet<>(); + private static HashMap users = new HashMap<>(); + + @OnOpen + public void onOpen(Session session, @PathParam("username") String username) throws IOException, EncodeException { + + this.session = session; + chatEndpoints.add(this); + users.put(session.getId(), username); + + Message message = new Message(); + message.setFrom(username); + message.setContent("Connected!"); + broadcast(message); + } + + @OnMessage + public void onMessage(Session session, Message message) throws IOException, EncodeException { + message.setFrom(users.get(session.getId())); + broadcast(message); + } + + @OnClose + public void onClose(Session session) throws IOException, EncodeException { + chatEndpoints.remove(this); + Message message = new Message(); + message.setFrom(users.get(session.getId())); + message.setContent("Disconnected!"); + broadcast(message); + } + + @OnError + public void onError(Session session, Throwable throwable) { + // Do error handling here + } + + private static void broadcast(Message message) throws IOException, EncodeException { + chatEndpoints.forEach(endpoint -> { + synchronized (endpoint) { + try { + endpoint.session.getBasicRemote() + .sendObject(message); + } catch (IOException | EncodeException e) { + e.printStackTrace(); + } + } + }); + } + +} diff --git a/java-websocket/src/main/java/com/baeldung/websocket/MessageDecoder.java b/java-websocket/src/main/java/com/baeldung/websocket/MessageDecoder.java new file mode 100644 index 000000000000..3bb3c4391d6f --- /dev/null +++ b/java-websocket/src/main/java/com/baeldung/websocket/MessageDecoder.java @@ -0,0 +1,34 @@ +package com.baeldung.websocket; + +import javax.websocket.DecodeException; +import javax.websocket.Decoder; +import javax.websocket.EndpointConfig; + +import com.baeldung.model.Message; +import com.google.gson.Gson; + +public class MessageDecoder implements Decoder.Text { + + private static Gson gson = new Gson(); + + @Override + public Message decode(String s) throws DecodeException { + Message message = gson.fromJson(s, Message.class); + return message; + } + + @Override + public boolean willDecode(String s) { + return (s != null); + } + + @Override + public void init(EndpointConfig endpointConfig) { + // Custom initialization logic + } + + @Override + public void destroy() { + // Close resources + } +} diff --git a/java-websocket/src/main/java/com/baeldung/websocket/MessageEncoder.java b/java-websocket/src/main/java/com/baeldung/websocket/MessageEncoder.java new file mode 100644 index 000000000000..6e7ba06ff096 --- /dev/null +++ b/java-websocket/src/main/java/com/baeldung/websocket/MessageEncoder.java @@ -0,0 +1,29 @@ +package com.baeldung.websocket; + +import javax.websocket.EncodeException; +import javax.websocket.Encoder; +import javax.websocket.EndpointConfig; + +import com.baeldung.model.Message; +import com.google.gson.Gson; + +public class MessageEncoder implements Encoder.Text { + + private static Gson gson = new Gson(); + + @Override + public String encode(Message message) throws EncodeException { + String json = gson.toJson(message); + return json; + } + + @Override + public void init(EndpointConfig endpointConfig) { + // Custom initialization logic + } + + @Override + public void destroy() { + // Close resources + } +} diff --git a/java-websocket/src/main/webapp/WEB-INF/beans.xml b/java-websocket/src/main/webapp/WEB-INF/beans.xml new file mode 100644 index 000000000000..73cd0d8e102a --- /dev/null +++ b/java-websocket/src/main/webapp/WEB-INF/beans.xml @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/java-websocket/src/main/webapp/WEB-INF/web.xml b/java-websocket/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000000..9f88c1f96324 --- /dev/null +++ b/java-websocket/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,7 @@ + + + + Archetype Created Web Application + diff --git a/java-websocket/src/main/webapp/index.html b/java-websocket/src/main/webapp/index.html new file mode 100644 index 000000000000..2b76fb1e497c --- /dev/null +++ b/java-websocket/src/main/webapp/index.html @@ -0,0 +1,30 @@ + + + Chat + + + + + + + + + + + + + +
+ + +
+ +
+ + +
+ + + + + \ No newline at end of file diff --git a/java-websocket/src/main/webapp/style.css b/java-websocket/src/main/webapp/style.css new file mode 100644 index 000000000000..ec0982005acc --- /dev/null +++ b/java-websocket/src/main/webapp/style.css @@ -0,0 +1,136 @@ +body { + font-family: Arial, Helvetica, sans-serif; + font-size: 80%; + background-color: #1f1f1f; +} + +#wrapper { + width: 960px; + margin: auto; + text-align: left; + color: #d9d9d9; +} + +p { + text-align: left; +} + +.button { + display: inline; + color: #fff; + background-color: #f2791d; + padding: 8px; + margin: auto; + border-radius: 8px; + -moz-border-radius: 8px; + -webkit-border-radius: 8px; + box-shadow: none; + border: none; +} + +.button:hover { + background-color: #ffb15e; +} +.button a, a:visited, a:hover, a:active { + color: #fff; + text-decoration: none; +} + +#addDevice { + text-align: center; + width: 960px; + margin: auto; + margin-bottom: 10px; +} + +#addDeviceForm { + text-align: left; + width: 400px; + margin: auto; + padding: 10px; +} + +#addDeviceForm span { + display: block; +} + +#content { + margin: auto; + width: 960px; +} + +.device { + width: 180px; + height: 110px; + margin: 10px; + padding: 16px; + color: #fff; + vertical-align: top; + border-radius: 8px; + -moz-border-radius: 8px; + -webkit-border-radius: 8px; + display: inline-block; +} + +.device.off { + background-color: #c8cccf; +} + +.device span { + display: block; +} + +.deviceName { + text-align: center; + font-weight: bold; + margin-bottom: 12px; +} + +.removeDevice { + margin-top: 12px; + text-align: center; +} + +.device.Appliance { + background-color: #5eb85e; +} + +.device.Appliance a:hover { + color: #a1ed82; +} + +.device.Electronics { + background-color: #0f90d1; +} + +.device.Electronics a:hover { + color: #4badd1; +} + +.device.Lights { + background-color: #c2a00c; +} + +.device.Lights a:hover { + color: #fad232; +} + +.device.Other { + background-color: #db524d; +} + +.device.Other a:hover { + color: #ff907d; +} + +.device a { + text-decoration: none; +} + +.device a:visited, a:active, a:hover { + color: #fff; +} + +.device a:hover { + text-decoration: underline; +} \ No newline at end of file diff --git a/java-websocket/src/main/webapp/websocket.js b/java-websocket/src/main/webapp/websocket.js new file mode 100644 index 000000000000..c23b2722fe3d --- /dev/null +++ b/java-websocket/src/main/webapp/websocket.js @@ -0,0 +1,26 @@ +var ws; + +function connect() { + var username = document.getElementById("username").value; + + var host = document.location.host; + var pathname = document.location.pathname; + + ws = new WebSocket("ws://" +host + pathname + "chat/" + username); + + ws.onmessage = function(event) { + var log = document.getElementById("log"); + console.log(event.data); + var message = JSON.parse(event.data); + log.innerHTML += message.from + " : " + message.content + "\n"; + }; +} + +function send() { + var content = document.getElementById("msg").value; + var json = JSON.stringify({ + "content":content + }); + + ws.send(json); +} \ No newline at end of file diff --git a/javaslang/README.md b/javaslang/README.md index e451883516e2..fe413fc2757c 100644 --- a/javaslang/README.md +++ b/javaslang/README.md @@ -2,3 +2,4 @@ - [Introduction to Javaslang](http://www.baeldung.com/javaslang) - [Guide to Try in Javaslang](http://www.baeldung.com/javaslang-try) - [Guide to Pattern Matching in Javaslang](http://www.baeldung.com/javaslang-pattern-matching) +- [Property Testing Example With Javaslang](http://www.baeldung.com/javaslang-property-testing) diff --git a/javaslang/pom.xml b/javaslang/pom.xml index e111132aec24..3770cce548b7 100644 --- a/javaslang/pom.xml +++ b/javaslang/pom.xml @@ -22,19 +22,40 @@ javaslang 2.1.0-alpha
+ + io.javaslang + javaslang-test + ${javaslang.test.version} + + + 2.0.5 + + org.apache.maven.plugins maven-compiler-plugin - 3.3 + 3.5.1 1.8 1.8 + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*IntegrationTest.java + **/*LongRunningUnitTest.java + **/*ManualTest.java + + true + + \ No newline at end of file diff --git a/javaslang/src/test/java/com/baeldung/javaslang/PropertyBasedLongRunningUnitTest.java b/javaslang/src/test/java/com/baeldung/javaslang/PropertyBasedLongRunningUnitTest.java new file mode 100644 index 000000000000..2bbc92563e02 --- /dev/null +++ b/javaslang/src/test/java/com/baeldung/javaslang/PropertyBasedLongRunningUnitTest.java @@ -0,0 +1,71 @@ +package com.baeldung.javaslang; + +import javaslang.CheckedFunction1; +import javaslang.collection.Stream; +import javaslang.test.Arbitrary; +import javaslang.test.CheckResult; +import javaslang.test.Property; +import org.junit.Test; + +import java.util.function.Predicate; + +import static javaslang.API.*; + +public class PropertyBasedLongRunningUnitTest { + + private static Predicate divisibleByTwo = i -> i % 2 == 0; + private static Predicate divisibleByFive = i -> i % 5 == 0; + + private Stream stringsSupplier() { + return Stream.from(0).map(i -> Match(i).of( + Case($(divisibleByFive.and(divisibleByTwo)), "DividedByTwoAndFiveWithoutRemainder"), + Case($(divisibleByFive), "DividedByFiveWithoutRemainder"), + Case($(divisibleByTwo), "DividedByTwoWithoutRemainder"), + Case($(), ""))); + } + + @Test + public void givenArbitrarySeq_whenCheckThatEverySecondElementIsEqualToString_thenTestPass() { + //given + Arbitrary multiplesOf2 = Arbitrary + .integer() + .filter(i -> i > 0) + .filter(i -> i % 2 == 0 && i % 5 != 0); + + //when + CheckedFunction1 mustEquals = i -> stringsSupplier() + .get(i) + .equals("DividedByTwoWithoutRemainder"); + + //then + CheckResult result = Property + .def("Every second element must equal to DividedByTwoWithoutRemainder") + .forAll(multiplesOf2) + .suchThat(mustEquals) + .check(10_000, 100); + + result.assertIsSatisfied(); + } + + @Test + public void givenArbitrarySeq_whenCheckThatEveryFifthElementIsEqualToString_thenTestPass() { + //given + Arbitrary multiplesOf5 = Arbitrary + .integer() + .filter(i -> i > 0) + .filter(i -> i % 5 == 0 && i % 2 == 0); + + //when + CheckedFunction1 mustEquals = i -> stringsSupplier() + .get(i) + .endsWith("DividedByTwoAndFiveWithoutRemainder"); + + //then + Property + .def("Every fifth element must equal to DividedByTwoAndFiveWithoutRemainder") + .forAll(multiplesOf5) + .suchThat(mustEquals) + .check(10_000, 1_000) + .assertIsSatisfied(); + } +} diff --git a/javax-servlets/pom.xml b/javax-servlets/pom.xml index 68a1b9c4413a..c5eeafbab3b9 100644 --- a/javax-servlets/pom.xml +++ b/javax-servlets/pom.xml @@ -49,6 +49,12 @@ httpclient ${org.apache.httpcomponents.version} test + + + commons-logging + commons-logging + + diff --git a/jaxb/src/main/resources/logback.xml b/jaxb/src/main/resources/logback.xml index 8b566286b88d..ec0dc2469ae0 100644 --- a/jaxb/src/main/resources/logback.xml +++ b/jaxb/src/main/resources/logback.xml @@ -1,18 +1,19 @@ + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - - %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg %n - - + + - - - + + - - - - + + + \ No newline at end of file diff --git a/jee7/README.md b/jee7/README.md index bc242c334069..8381bcbf3444 100644 --- a/jee7/README.md +++ b/jee7/README.md @@ -1,3 +1,4 @@ ### Relevant Articles: - [Scheduling in Java EE](http://www.baeldung.com/scheduling-in-java-enterprise-edition) - [JSON Processing in Java EE 7](http://www.baeldung.com/jee7-json) +- [Converters, Listeners and Validators in Java EE 7](http://www.baeldung.com/java-ee7-converter-listener-validator) diff --git a/jee7/pom.xml b/jee7/pom.xml index f275f56d58d6..e39865105565 100644 --- a/jee7/pom.xml +++ b/jee7/pom.xml @@ -4,10 +4,9 @@ 4.0.0 com.baeldung - jee7schedule + jee7 1.0-SNAPSHOT JavaEE 7 Arquillian Archetype Sample - war 1.8 @@ -28,6 +27,7 @@ 3.6.0 2.6 + 2.19.1 @@ -43,6 +43,13 @@ import pom + + org.jboss.arquillian.extension + arquillian-drone-bom + 2.0.1.Final + pom + import + @@ -65,6 +72,13 @@ arquillian-junit-container test + + org.jboss.arquillian.graphene + graphene-webdriver + 2.1.0.Final + pom + test + com.jayway.awaitility awaitility @@ -84,7 +98,47 @@ shrinkwrap-resolver-impl-maven-archive test - + + org.apache.httpcomponents + httpclient + 4.5 + + + commons-io + commons-io + 2.4 + + + com.sun.faces + jsf-api + 2.2.14 + + + com.sun.faces + jsf-impl + 2.2.14 + + + javax.servlet + jstl + 1.2 + + + javax.servlet + javax.servlet-api + 3.1.0 + + + javax.servlet.jsp + jsp-api + 2.2 + provided + + + taglibs + standard + 1.1.2 + @@ -167,6 +221,7 @@ + maven-dependency-plugin @@ -206,6 +261,7 @@
+
@@ -331,5 +387,21 @@ + + webdriver-chrome + + true + + + chrome + + + + + webdriver-firefox + + firefox + + diff --git a/jee7/src/main/java/com/baeldung/convListVal/ConvListVal.java b/jee7/src/main/java/com/baeldung/convListVal/ConvListVal.java new file mode 100644 index 000000000000..245c69f2bb20 --- /dev/null +++ b/jee7/src/main/java/com/baeldung/convListVal/ConvListVal.java @@ -0,0 +1,56 @@ +package com.baeldung.convListVal; + +import java.util.Date; + +import javax.faces.bean.ManagedBean; + +@ManagedBean +public class ConvListVal { + + private Integer age; + private Double average; + private Date myDate; + private String name; + private String surname; + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } + + public Double getAverage() { + return average; + } + + public void setAverage(Double average) { + this.average = average; + } + + public Date getMyDate() { + return myDate; + } + + public void setMyDate(Date myDate) { + this.myDate = myDate; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getSurname() { + return surname; + } + + public void setSurname(String surname) { + this.surname = surname; + } + +} diff --git a/jee7/src/main/java/com/baeldung/convListVal/MyListener.java b/jee7/src/main/java/com/baeldung/convListVal/MyListener.java new file mode 100644 index 000000000000..784bddd59aec --- /dev/null +++ b/jee7/src/main/java/com/baeldung/convListVal/MyListener.java @@ -0,0 +1,21 @@ +package com.baeldung.convListVal; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.faces.event.AbortProcessingException; +import javax.faces.event.ValueChangeEvent; +import javax.faces.event.ValueChangeListener; + +public class MyListener implements ValueChangeListener { + + private static final Logger LOG = Logger.getLogger(MyListener.class.getName()); + @Override + public void processValueChange(ValueChangeEvent event) throws AbortProcessingException { + if (event.getNewValue() != null) { + LOG.log(Level.INFO, "\tNew Value:{0}", event.getNewValue()); + } + + } + +} diff --git a/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/README.txt b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/README.txt new file mode 100644 index 000000000000..0f95e588b8c2 --- /dev/null +++ b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/README.txt @@ -0,0 +1,76 @@ +About the application +--------------------- +This application demonstrates the usage of JavaEE Web Annotations. + + +Contents of the application +--------------------------- +1. AccountServlet.java - Demonstrates the @WebServlet and @ServletSecurity annotation. + +NOTES: @WebServlet annotation designates the AccountServlet class as a Servlet component. + The usage of its parameters 'urlPatterns' & 'initParams' can be observed. + An initialization parameter 'type' is being set to denote the type of the bank account. + + @ServletSecurity annotation imposes security constraints on the AccountServlet based on + the tomcat-users.xml. +   + This code assumes that your tomcat-users.xml looks as follows: + + + + + + + +   +N.B : To see @ServletSecurity annotation in action, please uncomment the annotation code + for @ServletSecurity. + + +2. BankAppServletContextListener.java - Demonstrates the @WebListener annotation for denoting a class as a ServletContextListener. + +NOTES: Sets a Servlet context attribute ATTR_DEFAULT_LANGUAGE to 'english' on web application start up, + which can then be used throughout the application. + + +3. LogInFilter.java - Demonstrates the @WebFilter annotation. + +NOTES: @WebFilter annotation designates the LogInFilter class as a Filter component. + It filters all requests to the bank account servlet and redirects them to + the login page. + +N.B : To see @WebFilter annotation in action, please uncomment the annotation code for @WebFilter. + + +4. UploadCustomerDocumentsServlet.java - Demonstrates the @MultipartConfig annotation. + +NOTES: @MultipartConfig anotation designates the UploadCustomerDocumentsServlet Servlet component, + to handle multipart/form-data requests. + To see it in action, deploy the web application an access the url: http://:/JavaEEAnnotationsSample/upload.jsp + Once you upload a file from here, it will get uploaded to D:/custDocs (assuming such a folder exists). + + +5. index.jsp - This is the welcome page. + +NOTES: You can enter a deposit amount here and click on the 'Deposit' button to see the AccountServlet in action. + +6. login.jsp - All requests to the AccountServlet are redirected to this page, if the LogInFilter is imposed. + +7. upload.jsp - Demonstrates the usage of handling multipart/form-data requests by the UploadCustomerDocumentsServlet. + + +Building and Running the application +------------------------------------ +To build the application: + +1. Open the project in eclipse +2. Right click on it in eclispe and choose Run As > Maven build +3. Give 'clean install' under Goals +4. This should build the WAR file of the application + +To run the application: + +1. Right click on the project +2. Run as > Run on Server +3. This will start you Tomcat server and deploy the application (Provided that you have configured Tomcat in your eclipse) +4. You should now be able to access the url : http://:/JavaEEAnnotationsSample diff --git a/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/pom.xml b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/pom.xml new file mode 100644 index 000000000000..b4bb2435591c --- /dev/null +++ b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/pom.xml @@ -0,0 +1,57 @@ + + 4.0.0 + com.baeldung.javaeeannotations + JavaEEAnnotationsSample + 0.0.1-SNAPSHOT + war + JavaEEAnnotationsSample + JavaEEAnnotationsSample + + + + + javax.annotation + javax.annotation-api + 1.3 + + + + javax.servlet + javax.servlet-api + 3.1.0 + + + + javax.servlet.jsp + jsp-api + 2.1 + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.2 + + 1.7 + 1.7 + + + + org.apache.maven.plugins + maven-war-plugin + 2.4 + + src/main/webapp + SpringFieldConstructorInjection + false + + + + + JavaEEAnnotationsSample + + \ No newline at end of file diff --git a/jee7/src/main/java/com/baeldung/javaeeannotations/AccountServlet.java b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations/AccountServlet.java similarity index 74% rename from jee7/src/main/java/com/baeldung/javaeeannotations/AccountServlet.java rename to jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations/AccountServlet.java index e3f166759550..a487d4c3b170 100644 --- a/jee7/src/main/java/com/baeldung/javaeeannotations/AccountServlet.java +++ b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations/AccountServlet.java @@ -1,13 +1,10 @@ -package com.baeldung.javaeeannotations; +package com.baeldung.javaeeannotations.JavaEEAnnotationsSample.src.main.java.com.baeldung.javaeeannotations; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletConfig; import javax.servlet.ServletException; -import javax.servlet.annotation.HttpConstraint; -import javax.servlet.annotation.HttpMethodConstraint; -import javax.servlet.annotation.ServletSecurity; import javax.servlet.annotation.WebInitParam; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; @@ -19,39 +16,35 @@ urlPatterns = {"/account", "/bankAccount" }, initParams = { @WebInitParam(name = "type", value = "savings") } ) -@ServletSecurity( - value = @HttpConstraint(rolesAllowed = {"admin"}), - httpMethodConstraints = {@HttpMethodConstraint(value = "POST", rolesAllowed = {"admin"})} - ) +/*@ServletSecurity( + value = @HttpConstraint(rolesAllowed = {"Member"}), + httpMethodConstraints = {@HttpMethodConstraint(value = "POST", rolesAllowed = {"Admin"})} + )*/ public class AccountServlet extends javax.servlet.http.HttpServlet { String accountType = null; - @Override public void init(ServletConfig config) throws ServletException { accountType = config.getInitParameter("type"); } public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { + PrintWriter writer = response.getWriter(); writer.println("Hello, I am an AccountServlet!"); writer.flush(); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { + double accountBalance = 1000d; - double interestRate = Double.parseDouble(request.getAttribute("interest").toString()); - String paramDepositAmt = request.getParameter("dep"); double depositAmt = Double.parseDouble(paramDepositAmt); accountBalance = accountBalance + depositAmt; - + PrintWriter writer = response.getWriter(); - writer.println(" Balance of " + accountType + " account is: " + - accountBalance + "
This account bares an interest rate of " + interestRate + - " % "); + writer.println(" Balance of " + accountType + " account is: " + accountBalance + ""); writer.flush(); - } } diff --git a/jee7/src/main/java/com/baeldung/javaeeannotations/BankAppServletContextListener.java b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations/BankAppServletContextListener.java similarity index 82% rename from jee7/src/main/java/com/baeldung/javaeeannotations/BankAppServletContextListener.java rename to jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations/BankAppServletContextListener.java index 6b43dd8a84ac..dc9a91d05902 100644 --- a/jee7/src/main/java/com/baeldung/javaeeannotations/BankAppServletContextListener.java +++ b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations/BankAppServletContextListener.java @@ -1,17 +1,17 @@ -package com.baeldung.javaeeannotations; - -import javax.servlet.ServletContextEvent; -import javax.servlet.ServletContextListener; -import javax.servlet.annotation.WebListener; - -@WebListener -public class BankAppServletContextListener implements ServletContextListener { - - public void contextInitialized(ServletContextEvent sce) { - sce.getServletContext().setAttribute("ATTR_DEFAULT_LANGUAGE", "english"); - } - - public void contextDestroyed(ServletContextEvent sce) { - System.out.println("CONTEXT DESTROYED"); - } -} +package com.baeldung.javaeeannotations.JavaEEAnnotationsSample.src.main.java.com.baeldung.javaeeannotations; + +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; +import javax.servlet.annotation.WebListener; + +@WebListener +public class BankAppServletContextListener implements ServletContextListener { + + public void contextInitialized(ServletContextEvent sce) { + sce.getServletContext().setAttribute("ATTR_DEFAULT_LANGUAGE", "english"); + } + + public void contextDestroyed(ServletContextEvent sce) { + System.out.println("CONTEXT DESTROYED"); + } +} diff --git a/jee7/src/main/java/com/baeldung/javaeeannotations/LoggingFilter.java b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations/LogInFilter.java similarity index 80% rename from jee7/src/main/java/com/baeldung/javaeeannotations/LoggingFilter.java rename to jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations/LogInFilter.java index 97de5ec0de5a..bfe1a3937794 100644 --- a/jee7/src/main/java/com/baeldung/javaeeannotations/LoggingFilter.java +++ b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations/LogInFilter.java @@ -1,36 +1,36 @@ -package com.baeldung.javaeeannotations; - -import java.io.IOException; - -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.annotation.WebFilter; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -@WebFilter( - urlPatterns = "/bankAccount/*", - filterName = "LoggingFilter", - description = "Filter all account transaction URLs" - ) -public class LoggingFilter implements javax.servlet.Filter { - @Override - public void init(FilterConfig filterConfig) throws ServletException { - } - - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { - HttpServletRequest req = (HttpServletRequest) request; - HttpServletResponse res = (HttpServletResponse) response; - - res.sendRedirect(req.getContextPath() + "/login.jsp"); - chain.doFilter(request, response); - } - - @Override - public void destroy() { - } - -} +package com.baeldung.javaeeannotations.JavaEEAnnotationsSample.src.main.java.com.baeldung.javaeeannotations; + +import java.io.IOException; + +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.annotation.WebFilter; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/*@WebFilter( + urlPatterns = "/bankAccount/*", + filterName = "LogInFilter", + description = "Filter all account transaction URLs" + )*/ +public class LogInFilter implements javax.servlet.Filter { + + public void init(FilterConfig filterConfig) throws ServletException { + } + + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + HttpServletRequest req = (HttpServletRequest) request; + HttpServletResponse res = (HttpServletResponse) response; + + res.sendRedirect(req.getContextPath() + "/login.jsp"); + chain.doFilter(request, response); + } + + public void destroy() { + + } + +} diff --git a/jee7/src/main/java/com/baeldung/javaeeannotations/UploadCustomerDocumentsServlet.java b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations/UploadCustomerDocumentsServlet.java similarity index 74% rename from jee7/src/main/java/com/baeldung/javaeeannotations/UploadCustomerDocumentsServlet.java rename to jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations/UploadCustomerDocumentsServlet.java index 8a6c709b81eb..6945f663bbca 100644 --- a/jee7/src/main/java/com/baeldung/javaeeannotations/UploadCustomerDocumentsServlet.java +++ b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations/UploadCustomerDocumentsServlet.java @@ -1,29 +1,34 @@ -package com.baeldung.javaeeannotations; - -import java.io.IOException; - -import javax.servlet.ServletException; -import javax.servlet.annotation.MultipartConfig; -import javax.servlet.annotation.WebServlet; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.Part; - -@WebServlet(urlPatterns = { "/uploadCustDocs" }) -@MultipartConfig( - fileSizeThreshold = 1024 * 1024 * 20, - maxFileSize = 1024 * 1024 * 20, - maxRequestSize = 1024 * 1024 * 25, - location = "D:/custDocs" - ) -public class UploadCustomerDocumentsServlet extends HttpServlet { - - protected void doPost( - HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - for (Part part : request.getParts()) { - part.write("myFile"); - } - } - -} +package com.baeldung.javaeeannotations.JavaEEAnnotationsSample.src.main.java.com.baeldung.javaeeannotations; + +import java.io.IOException; +import java.io.PrintWriter; + +import javax.servlet.ServletException; +import javax.servlet.annotation.MultipartConfig; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.Part; + +@WebServlet(urlPatterns = { "/uploadCustDocs" }) +@MultipartConfig( + fileSizeThreshold = 1024 * 1024 * 20, + maxFileSize = 1024 * 1024 * 20, + maxRequestSize = 1024 * 1024 * 25, + location = "D:/custDocs" + ) +public class UploadCustomerDocumentsServlet extends HttpServlet { + + protected void doPost( + HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + for (Part part : request.getParts()) { + part.write("myFile"); + } + + PrintWriter writer = response.getWriter(); + writer.println("File uploaded successfully!"); + writer.flush(); + } + +} diff --git a/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/webapp/WEB-INF/web.xml b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000000..a92885ec117e --- /dev/null +++ b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,10 @@ + + + + BASIC + default + + diff --git a/jee7/src/main/webapp/index.jsp b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/webapp/index.jsp similarity index 79% rename from jee7/src/main/webapp/index.jsp rename to jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/webapp/index.jsp index 0c389ef5b1f3..c49dec859e9a 100644 --- a/jee7/src/main/webapp/index.jsp +++ b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/webapp/index.jsp @@ -1,16 +1,16 @@ -<%@ page language="java" contentType="text/html; charset=ISO-8859-1" - pageEncoding="ISO-8859-1"%> - - - - -My Account - - -
- Width: -    - -
- +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> + + + + +My Account + + +
+ Amount: +    + +
+ \ No newline at end of file diff --git a/jee7/src/main/webapp/login.jsp b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/webapp/login.jsp similarity index 97% rename from jee7/src/main/webapp/login.jsp rename to jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/webapp/login.jsp index 885df0c3d9bc..6892cb042084 100644 --- a/jee7/src/main/webapp/login.jsp +++ b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/webapp/login.jsp @@ -1,12 +1,12 @@ -<%@ page language="java" contentType="text/html; charset=ISO-8859-1" - pageEncoding="ISO-8859-1"%> - - - - -Login - - -Login Here... - +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> + + + + +Login + + +Login Here... + \ No newline at end of file diff --git a/jee7/src/main/webapp/upload.jsp b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/webapp/upload.jsp similarity index 97% rename from jee7/src/main/webapp/upload.jsp rename to jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/webapp/upload.jsp index 020483b99f78..3601322ef0a2 100644 --- a/jee7/src/main/webapp/upload.jsp +++ b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/webapp/upload.jsp @@ -1,16 +1,16 @@ -<%@ page language="java" contentType="text/html; charset=ISO-8859-1" - pageEncoding="ISO-8859-1"%> - - - - -Insert title here - - -
- -
- -
- +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> + + + + +Insert title here + + +
+ +
+ +
+ \ No newline at end of file diff --git a/jee7/src/main/java/com/baeldung/jaxws/EmployeeService.java b/jee7/src/main/java/com/baeldung/jaxws/EmployeeService.java new file mode 100644 index 000000000000..9735607da66e --- /dev/null +++ b/jee7/src/main/java/com/baeldung/jaxws/EmployeeService.java @@ -0,0 +1,32 @@ +package com.baeldung.jaxws; + +import java.util.List; + +import javax.jws.WebMethod; +import javax.jws.WebService; + +import com.baeldung.jaxws.exception.EmployeeAlreadyExists; +import com.baeldung.jaxws.exception.EmployeeNotFound; +import com.baeldung.jaxws.model.Employee; + +@WebService +public interface EmployeeService { + + @WebMethod + Employee getEmployee(int id) throws EmployeeNotFound; + + @WebMethod + Employee updateEmployee(int id, String name) throws EmployeeNotFound; + + @WebMethod + boolean deleteEmployee(int id) throws EmployeeNotFound; + + @WebMethod + Employee addEmployee(int id, String name) throws EmployeeAlreadyExists; + + @WebMethod + int countEmployees(); + + @WebMethod + List getAllEmployees(); +} diff --git a/jee7/src/main/java/com/baeldung/jaxws/EmployeeServiceImpl.java b/jee7/src/main/java/com/baeldung/jaxws/EmployeeServiceImpl.java new file mode 100644 index 000000000000..c1c9cd438599 --- /dev/null +++ b/jee7/src/main/java/com/baeldung/jaxws/EmployeeServiceImpl.java @@ -0,0 +1,49 @@ +package com.baeldung.jaxws; + +import java.util.List; + +import javax.inject.Inject; +import javax.jws.WebMethod; +import javax.jws.WebService; + +import com.baeldung.jaxws.exception.EmployeeAlreadyExists; +import com.baeldung.jaxws.exception.EmployeeNotFound; +import com.baeldung.jaxws.model.Employee; +import com.baeldung.jaxws.repository.EmployeeRepository; + +@WebService(serviceName = "EmployeeService", endpointInterface = "com.baeldung.jaxws.EmployeeService") +public class EmployeeServiceImpl implements EmployeeService { + + @Inject + private EmployeeRepository employeeRepositoryImpl; + + @WebMethod + public Employee getEmployee(int id) throws EmployeeNotFound { + return employeeRepositoryImpl.getEmployee(id); + } + + @WebMethod + public Employee updateEmployee(int id, String name) throws EmployeeNotFound { + return employeeRepositoryImpl.updateEmployee(id, name); + } + + @WebMethod + public boolean deleteEmployee(int id) throws EmployeeNotFound { + return employeeRepositoryImpl.deleteEmployee(id); + } + + @WebMethod + public Employee addEmployee(int id, String name) throws EmployeeAlreadyExists { + return employeeRepositoryImpl.addEmployee(id, name); + } + + @WebMethod + public int countEmployees() { + return employeeRepositoryImpl.count(); + } + + @WebMethod + public List getAllEmployees() { + return employeeRepositoryImpl.getAllEmployees(); + } +} diff --git a/jee7/src/main/java/com/baeldung/jaxws/config/EmployeeServicePublisher.java b/jee7/src/main/java/com/baeldung/jaxws/config/EmployeeServicePublisher.java new file mode 100644 index 000000000000..ac3b0493209b --- /dev/null +++ b/jee7/src/main/java/com/baeldung/jaxws/config/EmployeeServicePublisher.java @@ -0,0 +1,12 @@ +package com.baeldung.jaxws.config; + +import javax.xml.ws.Endpoint; + +import com.baeldung.jaxws.EmployeeServiceImpl; + +public class EmployeeServicePublisher { + + public static void main(String[] args) { + Endpoint.publish("http://localhost:8080/employeeservice", new EmployeeServiceImpl()); + } +} diff --git a/jee7/src/main/java/com/baeldung/jaxws/exception/EmployeeAlreadyExists.java b/jee7/src/main/java/com/baeldung/jaxws/exception/EmployeeAlreadyExists.java new file mode 100644 index 000000000000..8a96f8aec0ae --- /dev/null +++ b/jee7/src/main/java/com/baeldung/jaxws/exception/EmployeeAlreadyExists.java @@ -0,0 +1,15 @@ +package com.baeldung.jaxws.exception; + +import javax.xml.ws.WebFault; + +@WebFault +public class EmployeeAlreadyExists extends Exception { + + public EmployeeAlreadyExists() { + super("This employee already exists"); + } + + public EmployeeAlreadyExists(String str) { + super(str); + } +} diff --git a/jee7/src/main/java/com/baeldung/jaxws/exception/EmployeeNotFound.java b/jee7/src/main/java/com/baeldung/jaxws/exception/EmployeeNotFound.java new file mode 100644 index 000000000000..3de2ca8db6a6 --- /dev/null +++ b/jee7/src/main/java/com/baeldung/jaxws/exception/EmployeeNotFound.java @@ -0,0 +1,16 @@ +package com.baeldung.jaxws.exception; + +import javax.xml.ws.WebFault; + +@WebFault +public class EmployeeNotFound extends Exception { + + public EmployeeNotFound() { + super("The specified employee does not exist"); + } + + public EmployeeNotFound(String str) { + super(str); + } + +} diff --git a/jee7/src/main/java/com/baeldung/jaxws/model/Employee.java b/jee7/src/main/java/com/baeldung/jaxws/model/Employee.java new file mode 100644 index 000000000000..dbbdc234cf6c --- /dev/null +++ b/jee7/src/main/java/com/baeldung/jaxws/model/Employee.java @@ -0,0 +1,31 @@ +package com.baeldung.jaxws.model; + +public class Employee { + private int id; + private String firstName; + + public Employee() { + + } + + public Employee(int id, String firstName) { + this.id = id; + this.firstName = firstName; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } +} diff --git a/jee7/src/main/java/com/baeldung/jaxws/repository/EmployeeRepository.java b/jee7/src/main/java/com/baeldung/jaxws/repository/EmployeeRepository.java new file mode 100644 index 000000000000..0d5dec0462a4 --- /dev/null +++ b/jee7/src/main/java/com/baeldung/jaxws/repository/EmployeeRepository.java @@ -0,0 +1,22 @@ +package com.baeldung.jaxws.repository; + +import java.util.List; + +import com.baeldung.jaxws.exception.EmployeeAlreadyExists; +import com.baeldung.jaxws.exception.EmployeeNotFound; +import com.baeldung.jaxws.model.Employee; + +public interface EmployeeRepository { + + List getAllEmployees(); + + Employee getEmployee(int id) throws EmployeeNotFound; + + Employee updateEmployee(int id, String name) throws EmployeeNotFound; + + boolean deleteEmployee(int id) throws EmployeeNotFound; + + Employee addEmployee(int id, String name) throws EmployeeAlreadyExists; + + int count(); +} diff --git a/jee7/src/main/java/com/baeldung/jaxws/repository/EmployeeRepositoryImpl.java b/jee7/src/main/java/com/baeldung/jaxws/repository/EmployeeRepositoryImpl.java new file mode 100644 index 000000000000..f67509fff586 --- /dev/null +++ b/jee7/src/main/java/com/baeldung/jaxws/repository/EmployeeRepositoryImpl.java @@ -0,0 +1,68 @@ +package com.baeldung.jaxws.repository; + +import java.util.ArrayList; +import java.util.List; + +import com.baeldung.jaxws.exception.EmployeeAlreadyExists; +import com.baeldung.jaxws.exception.EmployeeNotFound; +import com.baeldung.jaxws.model.Employee; + +public class EmployeeRepositoryImpl implements EmployeeRepository { + private List employeeList; + + public EmployeeRepositoryImpl() { + employeeList = new ArrayList<>(); + employeeList.add(new Employee(1, "Jane")); + employeeList.add(new Employee(2, "Jack")); + employeeList.add(new Employee(3, "George")); + } + + public List getAllEmployees() { + return employeeList; + } + + public Employee getEmployee(int id) throws EmployeeNotFound { + for (Employee emp : employeeList) { + if (emp.getId() == id) { + return emp; + } + } + throw new EmployeeNotFound(); + } + + public Employee updateEmployee(int id, String name) throws EmployeeNotFound { + for (Employee employee1 : employeeList) { + if (employee1.getId() == id) { + employee1.setId(id); + employee1.setFirstName(name); + return employee1; + } + } + throw new EmployeeNotFound(); + } + + public boolean deleteEmployee(int id) throws EmployeeNotFound { + for (Employee emp : employeeList) { + if (emp.getId() == id) { + employeeList.remove(emp); + return true; + } + } + throw new EmployeeNotFound(); + } + + public Employee addEmployee(int id, String name) throws EmployeeAlreadyExists { + for (Employee emp : employeeList) { + if (emp.getId() == id) { + throw new EmployeeAlreadyExists(); + } + } + Employee employee = new Employee(id, name); + employeeList.add(employee); + return employee; + } + + public int count() { + return employeeList.size(); + } +} diff --git a/jee7/src/main/webapp/ConvListVal.xhtml b/jee7/src/main/webapp/ConvListVal.xhtml new file mode 100644 index 000000000000..8c5213764c01 --- /dev/null +++ b/jee7/src/main/webapp/ConvListVal.xhtml @@ -0,0 +1,50 @@ + + + + + Converters, Listeners and Validators + + +

Testing converters

+ + + + + + +
+ + + + + +
+ + + + + +
+ + + +
+ + + + +
+ + + + + + + +
+ +
+
+ diff --git a/jee7/src/main/webapp/WEB-INF/faces-config.xml b/jee7/src/main/webapp/WEB-INF/faces-config.xml new file mode 100644 index 000000000000..d5b2cd66123c --- /dev/null +++ b/jee7/src/main/webapp/WEB-INF/faces-config.xml @@ -0,0 +1,13 @@ + + + + convListVal + com.baeldung.convListVal.ConvListVal + session + + \ No newline at end of file diff --git a/jee7/src/main/webapp/WEB-INF/web.xml b/jee7/src/main/webapp/WEB-INF/web.xml index 0a3d84d2d474..11bd87cf9926 100644 --- a/jee7/src/main/webapp/WEB-INF/web.xml +++ b/jee7/src/main/webapp/WEB-INF/web.xml @@ -1,11 +1,41 @@ - - - - BASIC - default - - + + + + + Faces Servlet + javax.faces.webapp.FacesServlet + + + Faces Servlet + *.jsf + + + javax.faces.PROJECT_STAGE + Development + + + State saving method: 'client' or 'server' (default). See JSF Specification section 2.5.2 + javax.faces.STATE_SAVING_METHOD + client + + + + index.jsf + welcome.jsf + index.html + index.jsp + \ No newline at end of file diff --git a/jee7/src/test/java/com/baeldung/convListVal/ConvListValTest.java b/jee7/src/test/java/com/baeldung/convListVal/ConvListValTest.java new file mode 100644 index 000000000000..4450a26f4345 --- /dev/null +++ b/jee7/src/test/java/com/baeldung/convListVal/ConvListValTest.java @@ -0,0 +1,101 @@ +package com.baeldung.convListVal; + +import static org.jboss.arquillian.graphene.Graphene.guardHttp; + +import java.io.File; +import java.net.URL; + +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.container.test.api.RunAsClient; +import org.jboss.arquillian.drone.api.annotation.Drone; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.arquillian.test.api.ArquillianResource; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.EmptyAsset; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +@RunWith(Arquillian.class) +public class ConvListValTest { + + @ArquillianResource + private URL deploymentUrl; + + private static final String WEBAPP_SRC = "src/main/webapp"; + + @Deployment(testable = false) + public static WebArchive createDeployment() { + return ( ShrinkWrap.create( + WebArchive.class, "jee7.war"). + addClasses(ConvListVal.class, MyListener.class)). + addAsWebResource(new File(WEBAPP_SRC, "ConvListVal.xhtml")). + addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml"); + } + + @Drone + WebDriver browser; + + @ArquillianResource + URL contextPath; + + @FindBy(id="myForm:age") + private WebElement ageInput; + + @FindBy(id="myForm:average") + private WebElement averageInput; + + @FindBy(id="myForm:send") + private WebElement sendButton; + + @Test + @RunAsClient + public void givenAge_whenAgeInvalid_thenErrorMessage() throws Exception { + browser.get(deploymentUrl.toExternalForm() + "ConvListVal.jsf"); + ageInput.sendKeys("stringage"); + guardHttp(sendButton).click(); + Assert.assertTrue("Show Age error message", browser.findElements(By.id("myForm:ageError")).size() > 0); + } + + @Test + @RunAsClient + public void givenAverage_whenAverageInvalid_thenErrorMessage() throws Exception { + browser.get(deploymentUrl.toExternalForm() + "ConvListVal.jsf"); + averageInput.sendKeys("stringaverage"); + guardHttp(sendButton).click(); + Assert.assertTrue("Show Average error message", browser.findElements(By.id("myForm:averageError")).size() > 0); + } + + @Test + @RunAsClient + public void givenDate_whenDateInvalid_thenErrorMessage() throws Exception { + browser.get(deploymentUrl.toExternalForm() + "ConvListVal.jsf"); + averageInput.sendKeys("123"); + guardHttp(sendButton).click(); + Assert.assertTrue("Show Date error message", browser.findElements(By.id("myForm:myDateError")).size() > 0); + } + + @Test + @RunAsClient + public void givenSurname_whenSurnameMinLenghtInvalid_thenErrorMessage() throws Exception { + browser.get(deploymentUrl.toExternalForm() + "ConvListVal.jsf"); + averageInput.sendKeys("aaa"); + guardHttp(sendButton).click(); + Assert.assertTrue("Show Surname error message", browser.findElements(By.id("myForm:surnameError")).size() > 0); + } + + @Test + @RunAsClient + public void givenSurname_whenSurnameMaxLenghtInvalid_thenErrorMessage() throws Exception { + browser.get(deploymentUrl.toExternalForm() + "ConvListVal.jsf"); + averageInput.sendKeys("aaaaabbbbbc"); + guardHttp(sendButton).click(); + Assert.assertTrue("Show Surname error message", browser.findElements(By.id("myForm:surnameError")).size() > 0); + } + +} diff --git a/jee7/src/test/java/com/baeldung/jaxws/EmployeeServiceLiveTest.java b/jee7/src/test/java/com/baeldung/jaxws/EmployeeServiceLiveTest.java new file mode 100644 index 000000000000..44297034689a --- /dev/null +++ b/jee7/src/test/java/com/baeldung/jaxws/EmployeeServiceLiveTest.java @@ -0,0 +1,111 @@ +/*package com.baeldung.jaxws; + +import static org.junit.Assert.assertEquals; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.List; + +import javax.xml.namespace.QName; +import javax.xml.ws.Service; + +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.arquillian.test.api.ArquillianResource; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.EmptyAsset; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import com.baeldung.jaxws.exception.EmployeeAlreadyExists; +import com.baeldung.jaxws.exception.EmployeeNotFound; +import com.baeldung.jaxws.model.Employee; +import com.baeldung.jaxws.repository.EmployeeRepository; + +@RunWith(Arquillian.class) +public class EmployeeServiceLiveTest { + + private static final String APP_NAME = "jee7"; + private static final String WSDL_PATH = "EmployeeService?wsdl"; + private static QName SERVICE_NAME = new QName("http://jaxws.baeldung.com/", "EmployeeService"); + private static URL wsdlUrl; + + @ArquillianResource + private URL deploymentUrl; + + private EmployeeService employeeServiceProxy; + + @Deployment(testable = false) + public static WebArchive createDeployment() { + return ShrinkWrap.create(WebArchive.class, APP_NAME + ".war").addPackage(EmployeeService.class.getPackage()).addPackage(Employee.class.getPackage()).addPackage(EmployeeNotFound.class.getPackage()).addPackage(EmployeeRepository.class.getPackage()) + .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml"); + } + + @Before + public void setUp() { + try { + wsdlUrl = new URL(deploymentUrl, WSDL_PATH); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + + Service service = Service.create(wsdlUrl, SERVICE_NAME); + employeeServiceProxy = service.getPort(EmployeeService.class); + } + + @Test + public void givenEmployees_whenGetCount_thenCorrectNumberOfEmployeesReturned() { + int employeeCount = employeeServiceProxy.countEmployees(); + List employeeList = employeeServiceProxy.getAllEmployees(); + assertEquals(employeeList.size(), employeeCount); + } + + @Test + public void givenEmployees_whenGetAvailableEmployee_thenCorrectEmployeeReturned() throws EmployeeNotFound { + Employee employee = employeeServiceProxy.getEmployee(2); + assertEquals(employee.getFirstName(), "Jack"); + } + + @Test(expected = EmployeeNotFound.class) + public void givenEmployees_whenGetNonAvailableEmployee_thenEmployeeNotFoundException() throws EmployeeNotFound { + employeeServiceProxy.getEmployee(20); + } + + @Test + public void givenEmployees_whenAddNewEmployee_thenEmployeeCountIncreased() throws EmployeeAlreadyExists { + int employeeCount = employeeServiceProxy.countEmployees(); + employeeServiceProxy.addEmployee(4, "Anna"); + assertEquals(employeeServiceProxy.countEmployees(), employeeCount + 1); + } + + @Test(expected = EmployeeAlreadyExists.class) + public void givenEmployees_whenAddAlreadyExistingEmployee_thenEmployeeAlreadyExistsException() throws EmployeeAlreadyExists { + employeeServiceProxy.addEmployee(1, "Anna"); + } + + @Test + public void givenEmployees_whenUpdateExistingEmployee_thenUpdatedEmployeeReturned() throws EmployeeNotFound { + Employee updated = employeeServiceProxy.updateEmployee(1, "Joan"); + assertEquals(updated.getFirstName(), "Joan"); + } + + @Test(expected = EmployeeNotFound.class) + public void givenEmployees_whenUpdateNonExistingEmployee_thenEmployeeNotFoundException() throws EmployeeNotFound { + employeeServiceProxy.updateEmployee(20, "Joan"); + } + + @Test + public void givenEmployees_whenDeleteExistingEmployee_thenSuccessReturned() throws EmployeeNotFound { + boolean deleteEmployee = employeeServiceProxy.deleteEmployee(3); + assertEquals(deleteEmployee, true); + } + + @Test(expected = EmployeeNotFound.class) + public void givenEmployee_whenDeleteNonExistingEmployee_thenEmployeeNotFoundException() throws EmployeeNotFound { + employeeServiceProxy.deleteEmployee(20); + } + +} +*/ \ No newline at end of file diff --git a/jee7/src/test/java/com/baeldung/timer/ScheduleTimerBeanIntegrationTest.java b/jee7/src/test/java/com/baeldung/timer/ScheduleTimerBeanIntegrationTest.java index c3c5ad44de8b..580edade77d3 100644 --- a/jee7/src/test/java/com/baeldung/timer/ScheduleTimerBeanIntegrationTest.java +++ b/jee7/src/test/java/com/baeldung/timer/ScheduleTimerBeanIntegrationTest.java @@ -19,25 +19,27 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; - @RunWith(Arquillian.class) public class ScheduleTimerBeanIntegrationTest { - final static long TIMEOUT = 5000l; - final static long TOLERANCE = 1000l; + private final static long TIMEOUT = 5000l; + private final static long TOLERANCE = 1000l; - @Inject - TimerEventListener timerEventListener; + @Inject TimerEventListener timerEventListener; @Deployment public static WebArchive deploy() { - File[] jars = Maven.resolver().loadPomFromFile("pom.xml") - .resolve("com.jayway.awaitility:awaitility") - .withTransitivity().asFile(); + File[] jars = Maven + .resolver() + .loadPomFromFile("pom.xml") + .resolve("com.jayway.awaitility:awaitility") + .withTransitivity() + .asFile(); - return ShrinkWrap.create(WebArchive.class) - .addAsLibraries(jars) - .addClasses(WithinWindowMatcher.class, TimerEvent.class, TimerEventListener.class, ScheduleTimerBean.class); + return ShrinkWrap + .create(WebArchive.class) + .addAsLibraries(jars) + .addClasses(WithinWindowMatcher.class, TimerEvent.class, TimerEventListener.class, ScheduleTimerBean.class); } @Test @@ -46,9 +48,15 @@ public void should_receive_three_pings() { Awaitility.setDefaultTimeout(30, TimeUnit.SECONDS); await().untilCall(to(timerEventListener.getEvents()).size(), equalTo(3)); - TimerEvent firstEvent = timerEventListener.getEvents().get(0); - TimerEvent secondEvent = timerEventListener.getEvents().get(1); - TimerEvent thirdEvent = timerEventListener.getEvents().get(2); + TimerEvent firstEvent = timerEventListener + .getEvents() + .get(0); + TimerEvent secondEvent = timerEventListener + .getEvents() + .get(1); + TimerEvent thirdEvent = timerEventListener + .getEvents() + .get(2); long delay = secondEvent.getTime() - firstEvent.getTime(); assertThat(delay, Matchers.is(WithinWindowMatcher.withinWindow(TIMEOUT, TOLERANCE))); diff --git a/jhipster/.editorconfig b/jhipster/.editorconfig new file mode 100644 index 000000000000..a03599dd0422 --- /dev/null +++ b/jhipster/.editorconfig @@ -0,0 +1,24 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + +[*] + +# Change these settings to your own preference +indent_style = space +indent_size = 4 + +# We recommend you to keep these unchanged +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false + +[{package,bower}.json] +indent_style = space +indent_size = 2 diff --git a/jhipster/.gitattributes b/jhipster/.gitattributes new file mode 100644 index 000000000000..2a13efa88f5a --- /dev/null +++ b/jhipster/.gitattributes @@ -0,0 +1,22 @@ +# All text files should have the "lf" (Unix) line endings +* text eol=lf + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.java text +*.js text +*.css text +*.html text + +# Denote all files that are truly binary and should not be modified. +*.png binary +*.jpg binary +*.jar binary +*.pdf binary +*.eot binary +*.ttf binary +*.gzip binary +*.gz binary +*.ai binary +*.eps binary +*.swf binary diff --git a/jhipster/.gitignore b/jhipster/.gitignore new file mode 100644 index 000000000000..c9f735a4965f --- /dev/null +++ b/jhipster/.gitignore @@ -0,0 +1,143 @@ +###################### +# Project Specific +###################### +/src/main/webapp/content/css/main.css +/target/www/** +/src/test/javascript/coverage/ +/src/test/javascript/PhantomJS*/ + +###################### +# Node +###################### +/node/ +node_tmp/ +node_modules/ +npm-debug.log.* + +###################### +# SASS +###################### +.sass-cache/ + +###################### +# Eclipse +###################### +*.pydevproject +.project +.metadata +tmp/ +tmp/**/* +*.tmp +*.bak +*.swp +*~.nib +local.properties +.classpath +.settings/ +.loadpath +.factorypath +/src/main/resources/rebel.xml + +# External tool builders +.externalToolBuilders/** + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + +###################### +# Intellij +###################### +.idea/ +*.iml +*.iws +*.ipr +*.ids +*.orig + +###################### +# Visual Studio Code +###################### +.vscode/ + +###################### +# Maven +###################### +/log/ +/target/ + +###################### +# Gradle +###################### +.gradle/ +/build/ + +###################### +# Package Files +###################### +*.jar +*.war +*.ear +*.db + +###################### +# Windows +###################### +# Windows image file caches +Thumbs.db + +# Folder config file +Desktop.ini + +###################### +# Mac OSX +###################### +.DS_Store +.svn + +# Thumbnails +._* + +# Files that might appear on external disk +.Spotlight-V100 +.Trashes + +###################### +# Directories +###################### +/bin/ +/deploy/ + +###################### +# Logs +###################### +*.log + +###################### +# Others +###################### +*.class +*.*~ +*~ +.merge_file* + +###################### +# Gradle Wrapper +###################### +!gradle/wrapper/gradle-wrapper.jar + +###################### +# Maven Wrapper +###################### +!.mvn/wrapper/maven-wrapper.jar + +###################### +# ESLint +###################### +.eslintcache +/.apt_generated/ diff --git a/jhipster/.gitlab-ci.yml b/jhipster/.gitlab-ci.yml new file mode 100644 index 000000000000..1cf574251a54 --- /dev/null +++ b/jhipster/.gitlab-ci.yml @@ -0,0 +1,56 @@ + +cache: + key: "$CI_BUILD_REF_NAME" + paths: + - node_modules + - .maven +stages: + - build + - test + - package + +before_script: + - export MAVEN_USER_HOME=`pwd`/.maven + - chmod +x mvnw + - ./mvnw com.github.eirslett:frontend-maven-plugin:install-node-and-npm -DnodeVersion=v6.10.0 -DnpmVersion=4.3.0 + - ./mvnw com.github.eirslett:frontend-maven-plugin:npm + +maven-build: + stage: build + script: ./mvnw compile -Dmaven.repo.local=$MAVEN_USER_HOME + +maven-test: + stage: test + script: + - ./mvnw test -Dmaven.repo.local=$MAVEN_USER_HOME + artifacts: + paths: + - target/surefire-reports/* +maven-front-test: + stage: test + script: + - ./mvnw com.github.eirslett:frontend-maven-plugin:npm -Dfrontend.yarn.arguments=test + artifacts: + paths: + - target/test-results/karma/* +gatling-test: + stage: test + allow_failure: true + script: + - ./mvnw gatling:execute -Dmaven.repo.local=$MAVEN_USER_HOME + before_script: + - export MAVEN_USER_HOME=`pwd`/.maven + - chmod +x mvnw + - ./mvnw com.github.eirslett:frontend-maven-plugin:install-node-and-npm -DnodeVersion=v6.10.0 -DnpmVersion=4.3.0 + - ./mvnw com.github.eirslett:frontend-maven-plugin:npm + - ./mvnw & + artifacts: + paths: + - target/gatling/* +maven-package: + stage: package + script: + - ./mvnw package -Pprod -DskipTests -Dmaven.repo.local=$MAVEN_USER_HOME + artifacts: + paths: + - target/*.war diff --git a/jhipster/.jhipster/Comment.json b/jhipster/.jhipster/Comment.json new file mode 100644 index 000000000000..c6022daa60a7 --- /dev/null +++ b/jhipster/.jhipster/Comment.json @@ -0,0 +1,39 @@ +{ + "fluentMethods": true, + "relationships": [ + { + "relationshipName": "post", + "otherEntityName": "post", + "relationshipType": "many-to-one", + "relationshipValidateRules": [ + "required" + ], + "otherEntityField": "title" + } + ], + "fields": [ + { + "fieldName": "text", + "fieldType": "String", + "fieldValidateRules": [ + "required", + "minlength", + "maxlength" + ], + "fieldValidateRulesMinlength": "10", + "fieldValidateRulesMaxlength": "100" + }, + { + "fieldName": "creationDate", + "fieldType": "LocalDate", + "fieldValidateRules": [ + "required" + ] + } + ], + "changelogDate": "20170316224021", + "dto": "no", + "service": "no", + "entityTableName": "comment", + "pagination": "infinite-scroll" +} diff --git a/jhipster/.jhipster/Post.json b/jhipster/.jhipster/Post.json new file mode 100644 index 000000000000..595cf435980d --- /dev/null +++ b/jhipster/.jhipster/Post.json @@ -0,0 +1,52 @@ +{ + "fluentMethods": true, + "relationships": [ + { + "relationshipName": "creator", + "otherEntityName": "user", + "relationshipType": "many-to-one", + "relationshipValidateRules": [ + "required" + ], + "otherEntityField": "login", + "ownerSide": true, + "otherEntityRelationshipName": "post" + } + ], + "fields": [ + { + "fieldName": "title", + "fieldType": "String", + "fieldValidateRules": [ + "required", + "minlength", + "maxlength" + ], + "fieldValidateRulesMinlength": "10", + "fieldValidateRulesMaxlength": "100" + }, + { + "fieldName": "content", + "fieldType": "String", + "fieldValidateRules": [ + "required", + "minlength", + "maxlength" + ], + "fieldValidateRulesMinlength": "10", + "fieldValidateRulesMaxlength": "1000" + }, + { + "fieldName": "creationDate", + "fieldType": "LocalDate", + "fieldValidateRules": [ + "required" + ] + } + ], + "changelogDate": "20170316223211", + "dto": "no", + "service": "no", + "entityTableName": "post", + "pagination": "infinite-scroll" +} diff --git a/jhipster/.mvn/wrapper/maven-wrapper.jar b/jhipster/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 000000000000..5fd4d5023f14 Binary files /dev/null and b/jhipster/.mvn/wrapper/maven-wrapper.jar differ diff --git a/jhipster/.mvn/wrapper/maven-wrapper.properties b/jhipster/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 000000000000..c954cec91ccb --- /dev/null +++ b/jhipster/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip diff --git a/jhipster/.travis.yml b/jhipster/.travis.yml new file mode 100644 index 000000000000..c34d1a7da680 --- /dev/null +++ b/jhipster/.travis.yml @@ -0,0 +1,41 @@ +os: + - linux +services: + - docker +language: node_js +node_js: + - "6.10.0" +jdk: + - oraclejdk8 +sudo: false +cache: + directories: + - node + - node_modules + - $HOME/.m2 +env: + global: + - NODE_VERSION=6.10.0 + - SPRING_OUTPUT_ANSI_ENABLED=ALWAYS + - SPRING_JPA_SHOW_SQL=false +before_install: + - jdk_switcher use oraclejdk8 + - java -version + - sudo /etc/init.d/mysql stop + - sudo /etc/init.d/postgresql stop + - nvm install $NODE_VERSION + - npm install -g npm + - node -v + - npm -v +install: + - npm install +script: + - chmod +x mvnw + - ./mvnw clean test + - npm test + - ./mvnw package -Pprod -DskipTests +notifications: + webhooks: + on_success: change # options: [always|never|change] default: always + on_failure: always # options: [always|never|change] default: always + on_start: false # default: false diff --git a/jhipster/.yo-rc.json b/jhipster/.yo-rc.json new file mode 100644 index 000000000000..155e33b1c5f5 --- /dev/null +++ b/jhipster/.yo-rc.json @@ -0,0 +1,36 @@ +{ + "generator-jhipster": { + "jhipsterVersion": "4.0.8", + "baseName": "baeldung", + "packageName": "com.baeldung", + "packageFolder": "com/baeldung", + "serverPort": "8080", + "authenticationType": "jwt", + "hibernateCache": "ehcache", + "clusteredHttpSession": false, + "websocket": false, + "databaseType": "sql", + "devDatabaseType": "h2Disk", + "prodDatabaseType": "mysql", + "searchEngine": false, + "messageBroker": false, + "serviceDiscoveryType": false, + "buildTool": "maven", + "enableSocialSignIn": false, + "jwtSecretKey": "e1d4b69d3f953e3fa622121e882e6f459ca20ca4", + "clientFramework": "angular2", + "useSass": true, + "clientPackageManager": "npm", + "applicationType": "monolith", + "testFrameworks": [ + "gatling", + "protractor" + ], + "jhiPrefix": "jhi", + "enableTranslation": true, + "nativeLanguage": "en", + "languages": [ + "en" + ] + } +} \ No newline at end of file diff --git a/jhipster/Jenkinsfile b/jhipster/Jenkinsfile new file mode 100644 index 000000000000..1f0873a47284 --- /dev/null +++ b/jhipster/Jenkinsfile @@ -0,0 +1,50 @@ +#!/usr/bin/env groovy + +node { + stage('checkout') { + checkout scm + } + + stage('check java') { + sh "java -version" + } + + stage('clean') { + sh "chmod +x mvnw" + sh "./mvnw clean" + } + + stage('install tools') { + sh "./mvnw com.github.eirslett:frontend-maven-plugin:install-node-and-npm -DnodeVersion=v6.10.0 -DnpmVersion=4.3.0" + } + + stage('npm install') { + sh "./mvnw com.github.eirslett:frontend-maven-plugin:npm" + } + + stage('backend tests') { + try { + sh "./mvnw test" + } catch(err) { + throw err + } finally { + junit '**/target/surefire-reports/TEST-*.xml' + } + } + + stage('frontend tests') { + try { + sh "./mvnw com.github.eirslett:frontend-maven-plugin:npm -Dfrontend.yarn.arguments=test" + } catch(err) { + throw err + } finally { + junit '**/target/test-results/karma/TESTS-*.xml' + } + } + + stage('packaging') { + sh "./mvnw package -Pprod -DskipTests" + archiveArtifacts artifacts: '**/target/*.war', fingerprint: true + } + +} diff --git a/jhipster/README.md b/jhipster/README.md new file mode 100644 index 000000000000..d321e9e81ec4 --- /dev/null +++ b/jhipster/README.md @@ -0,0 +1,158 @@ +### Relevant articles + +- [Intro to JHipster](http://www.baeldung.com/jhipster) + + +# baeldung +This application was generated using JHipster 4.0.8, you can find documentation and help at [https://jhipster.github.io/documentation-archive/v4.0.8](https://jhipster.github.io/documentation-archive/v4.0.8). + +## Development + +Before you can build this project, you must install and configure the following dependencies on your machine: + +1. [Node.js][]: We use Node to run a development web server and build the project. + Depending on your system, you can install Node either from source or as a pre-packaged bundle. + +After installing Node, you should be able to run the following command to install development tools. +You will only need to run this command when dependencies change in [package.json](package.json). + + npm install + +We use npm scripts and [Webpack][] as our build system. + + +Run the following commands in two separate terminals to create a blissful development experience where your browser +auto-refreshes when files change on your hard drive. + + ./mvnw + npm start + +[Npm][] is also used to manage CSS and JavaScript dependencies used in this application. You can upgrade dependencies by +specifying a newer version in [package.json](package.json). You can also run `npm update` and `npm install` to manage dependencies. +Add the `help` flag on any command to see how you can use it. For example, `npm help update`. + +The `npm run` command will list all of the scripts available to run for this project. + +### Managing dependencies + +For example, to add [Leaflet][] library as a runtime dependency of your application, you would run following command: + + npm install --save --save-exact leaflet + +To benefit from TypeScript type definitions from [DefinitelyTyped][] repository in development, you would run following command: + + npm install --save-dev --save-exact @types/leaflet + +Then you would import the JS and CSS files specified in library's installation instructions so that [Webpack][] knows about them: + +Edit [src/main/webapp/app/vendor.ts](src/main/webapp/app/vendor.ts) file: +~~~ +import 'leaflet/dist/leaflet.js'; +~~~ + +Edit [src/main/webapp/content/css/vendor.css](src/main/webapp/content/css/vendor.css) file: +~~~ +@import '~leaflet/dist/leaflet.css'; +~~~ + +Note: there are still few other things remaining to do for Leaflet that we won't detail here. + +For further instructions on how to develop with JHipster, have a look at [Using JHipster in development][]. + +### Using angular-cli + +You can also use [Angular CLI][] to generate some custom client code. + +For example, the following command: + + ng generate component my-component + +will generate few files: + + create src/main/webapp/app/my-component/my-component.component.html + create src/main/webapp/app/my-component/my-component.component.ts + update src/main/webapp/app/app.module.ts + +## Building for production + +To optimize the baeldung application for production, run: + + ./mvnw -Pprod clean package + +This will concatenate and minify the client CSS and JavaScript files. It will also modify `index.html` so it references these new files. +To ensure everything worked, run: + + java -jar target/*.war + +Then navigate to [http://localhost:8080](http://localhost:8080) in your browser. + +Refer to [Using JHipster in production][] for more details. + +## Testing + +To launch your application's tests, run: + + ./mvnw clean test + +### Client tests + +Unit tests are run by [Karma][] and written with [Jasmine][]. They're located in [src/test/javascript/](src/test/javascript/) and can be run with: + + npm test + +UI end-to-end tests are powered by [Protractor][], which is built on top of WebDriverJS. They're located in [src/test/javascript/e2e](src/test/javascript/e2e) +and can be run by starting Spring Boot in one terminal (`./mvnw spring-boot:run`) and running the tests (`gulp itest`) in a second one. +### Other tests + +Performance tests are run by [Gatling][] and written in Scala. They're located in [src/test/gatling](src/test/gatling) and can be run with: + + ./mvnw gatling:execute + +For more information, refer to the [Running tests page][]. + +## Using Docker to simplify development (optional) + +You can use Docker to improve your JHipster development experience. A number of docker-compose configuration are available in the [src/main/docker](src/main/docker) folder to launch required third party services. +For example, to start a mysql database in a docker container, run: + + docker-compose -f src/main/docker/mysql.yml up -d + +To stop it and remove the container, run: + + docker-compose -f src/main/docker/mysql.yml down + +You can also fully dockerize your application and all the services that it depends on. +To achieve this, first build a docker image of your app by running: + + ./mvnw package -Pprod docker:build + +Then run: + + docker-compose -f src/main/docker/app.yml up -d + +For more information refer to [Using Docker and Docker-Compose][], this page also contains information on the docker-compose sub-generator (`yo jhipster:docker-compose`), which is able to generate docker configurations for one or several JHipster applications. + +## Continuous Integration (optional) + +To configure CI for your project, run the ci-cd sub-generator (`yo jhipster:ci-cd`), this will let you generate configuration files for a number of Continuous Integration systems. Consult the [Setting up Continuous Integration][] page for more information. + +[JHipster Homepage and latest documentation]: https://jhipster.github.io +[JHipster 4.0.8 archive]: https://jhipster.github.io/documentation-archive/v4.0.8 + +[Using JHipster in development]: https://jhipster.github.io/documentation-archive/v4.0.8/development/ +[Using Docker and Docker-Compose]: https://jhipster.github.io/documentation-archive/v4.0.8/docker-compose +[Using JHipster in production]: https://jhipster.github.io/documentation-archive/v4.0.8/production/ +[Running tests page]: https://jhipster.github.io/documentation-archive/v4.0.8/running-tests/ +[Setting up Continuous Integration]: https://jhipster.github.io/documentation-archive/v4.0.8/setting-up-ci/ + +[Gatling]: http://gatling.io/ +[Node.js]: https://nodejs.org/ +[Yarn]: https://yarnpkg.org/ +[Webpack]: https://webpack.github.io/ +[Angular CLI]: https://cli.angular.io/ +[BrowserSync]: http://www.browsersync.io/ +[Karma]: http://karma-runner.github.io/ +[Jasmine]: http://jasmine.github.io/2.0/introduction.html +[Protractor]: https://angular.github.io/protractor/ +[Leaflet]: http://leafletjs.com/ +[DefinitelyTyped]: http://definitelytyped.org/ diff --git a/jhipster/angular-cli.json b/jhipster/angular-cli.json new file mode 100644 index 000000000000..15558a2faea5 --- /dev/null +++ b/jhipster/angular-cli.json @@ -0,0 +1,55 @@ +{ + "project": { + "version": "1.0.0-beta.24", + "name": "baeldung" + }, + "apps": [ + { + "root": "src/main/webapp/", + "outDir": "target/www/app", + "assets": [ + "content", + "favicon.ico" + ], + "index": "index.html", + "main": "app/app.main.ts", + "test": "", + "tsconfig": "../../../tsconfig.json", + "prefix": "jhi", + "mobile": false, + "styles": [ + "content/css/main.css" + ], + "scripts": [], + "environments": {} + } + ], + "addons": [], + "packages": [], + "e2e": { + "protractor": { + "config": "src/test/javascript/protractor.conf.js" + } + }, + "test": { + "karma": { + "config": "src/test/javascript/karma.conf.js" + } + }, + "defaults": { + "styleExt": "css", + "prefixInterfaces": false, + "inline": { + "style": true, + "template": false + }, + "spec": { + "class": false, + "component": false, + "directive": false, + "module": false, + "pipe": false, + "service": false + } + } +} diff --git a/jhipster/circle.yml b/jhipster/circle.yml new file mode 100644 index 000000000000..bc9371e94fc8 --- /dev/null +++ b/jhipster/circle.yml @@ -0,0 +1,25 @@ +machine: + services: + - docker + java: + version: oraclejdk8 + node: + version: 6.10.0 +dependencies: + cache_directories: + - node + - node_modules + - ~/.m2 + override: + - java -version + - npm install -g npm + - node -v + - npm -v + - java -version + - npm install +test: + override: + - chmod +x mvnw + - ./mvnw clean test + - npm test + - ./mvnw package -Pprod -DskipTests diff --git a/jhipster/mvnw b/jhipster/mvnw new file mode 100755 index 000000000000..a1ba1bf554bb --- /dev/null +++ b/jhipster/mvnw @@ -0,0 +1,233 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # + # Look for the Apple JDKs first to preserve the existing behaviour, and then look + # for the new JDKs provided by Oracle. + # + if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then + # + # Apple JDKs + # + export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then + # + # Apple JDKs + # + export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then + # + # Oracle JDKs + # + export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then + # + # Apple JDKs + # + export JAVA_HOME=`/usr/libexec/java_home` + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Migwn, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + # TODO classpath? +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + local basedir=$(pwd) + local wdir=$(pwd) + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + wdir=$(cd "$wdir/.."; pwd) + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)} +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} "$@" diff --git a/jhipster/mvnw.cmd b/jhipster/mvnw.cmd new file mode 100644 index 000000000000..2b934e89dd1d --- /dev/null +++ b/jhipster/mvnw.cmd @@ -0,0 +1,145 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +set MAVEN_CMD_LINE_ARGS=%* + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" + +set WRAPPER_JAR="".\.mvn\wrapper\maven-wrapper.jar"" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS% +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% \ No newline at end of file diff --git a/jhipster/package.json b/jhipster/package.json new file mode 100644 index 000000000000..40bbd28799e0 --- /dev/null +++ b/jhipster/package.json @@ -0,0 +1,112 @@ +{ + "name": "baeldung", + "version": "0.0.0", + "description": "Description for baeldung", + "private": true, + "cacheDirectories": [ + "node_modules" + ], + "dependencies": { + "@angular/common": "2.4.7", + "@angular/compiler": "2.4.7", + "@angular/core": "2.4.7", + "@angular/forms": "2.4.7", + "@angular/http": "2.4.7", + "@angular/platform-browser": "2.4.7", + "@angular/platform-browser-dynamic": "2.4.7", + "@angular/router": "3.4.7", + "@ng-bootstrap/ng-bootstrap": "1.0.0-alpha.20", + "angular2-infinite-scroll": "0.3.0", + "bootstrap": "4.0.0-alpha.6", + "font-awesome": "4.7.0", + "angular2-cookie": "1.2.6", + "core-js": "2.4.1", + "jquery": "3.1.1", + "ng-jhipster": "0.1.9", + "ng2-webstorage": "1.5.0", + "reflect-metadata": "0.1.9", + "rxjs": "5.1.0", + "swagger-ui": "2.2.10", + "tether": "1.4.0", + "zone.js": "0.7.6" + }, + "devDependencies": { + "@angular/cli": "1.0.0-beta.28.3", + "@types/jasmine": "2.5.42", + "@types/node": "7.0.5", + "@types/selenium-webdriver": "2.53.39", + "add-asset-html-webpack-plugin": "1.0.2", + "angular2-template-loader": "0.6.2", + "awesome-typescript-loader": "3.0.7", + "browser-sync": "2.18.7", + "browser-sync-webpack-plugin": "1.1.4", + "codelyzer": "2.0.0", + "copy-webpack-plugin": "4.0.1", + "css-loader": "0.26.1", + "del": "2.2.2", + "event-stream": "3.3.4", + "exports-loader": "0.6.3", + "extract-text-webpack-plugin": "2.0.0-beta.5", + "file-loader": "0.10.0", + "generator-jhipster": "4.0.8", + "html-webpack-plugin": "2.28.0", + "image-webpack-loader": "3.2.0", + "jasmine-core": "2.5.2", + "jasmine-reporters": "2.2.0", + "karma": "1.4.1", + "karma-chrome-launcher": "2.0.0", + "karma-coverage": "1.1.1", + "karma-intl-shim": "1.0.3", + "karma-jasmine": "1.1.0", + "karma-junit-reporter": "1.2.0", + "karma-phantomjs-launcher": "1.0.2", + "karma-remap-istanbul": "0.6.0", + "karma-sourcemap-loader": "0.3.7", + "karma-webpack": "2.0.2", + "lazypipe": "1.0.1", + "lodash": "4.17.4", + "map-stream": "0.0.6", + "phantomjs-prebuilt": "2.1.14", + "protractor": "5.1.1", + "protractor-jasmine2-screenshot-reporter": "0.3.3", + "ts-node": "2.1.0", + "proxy-middleware": "0.15.0", + "raw-loader": "0.5.1", + "run-sequence": "1.2.2", + "sourcemap-istanbul-instrumenter-loader": "0.2.0", + "string-replace-webpack-plugin": "0.0.5", + "style-loader": "0.13.1", + "to-string-loader": "1.1.5", + "tslint": "4.4.2", + "tslint-loader": "3.4.1", + "typescript": "2.1.6", + "webpack": "2.2.1", + "webpack-dev-server": "2.3.0", + "webpack-merge": "2.6.1", + "webpack-visualizer-plugin": "0.1.10", + "write-file-webpack-plugin": "3.4.2", + "xml2js": "0.4.17", + "sass-loader": "5.0.1", + "node-sass": "4.5.0", + "postcss-loader": "1.3.0", + "yargs": "6.6.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "scripts": { + "lint": "tslint 'src/main/webapp/app/**/*.ts' --force", + "lint:fix": "tslint 'src/main/webapp/app/**/*.ts' --fix --force", + "tsc": "tsc", + "tsc:w": "tsc -w", + "start": "npm run webpack:dev", + "webpack:build": "webpack --config webpack/webpack.vendor.js && webpack --config webpack/webpack.dev.js", + "webpack:build:dev": "webpack --config webpack/webpack.dev.js", + "webpack:dev": "webpack-dev-server --config webpack/webpack.dev.js --progress --inline --hot --profile --port=9060", + "webpack:prod": "npm test && webpack -p --config webpack/webpack.vendor.js && webpack -p --config webpack/webpack.prod.js", + "test": "npm run lint && karma start src/test/javascript/karma.conf.js", + "test:watch": "karma start --watch", + "e2e": "protractor src/test/javascript/protractor.conf.js", + "postinstall": "webdriver-manager update && npm run webpack:build" + } +} diff --git a/jhipster/pom.xml b/jhipster/pom.xml new file mode 100644 index 000000000000..ba78ad4e2b0c --- /dev/null +++ b/jhipster/pom.xml @@ -0,0 +1,1034 @@ + + + 4.0.0 + + + spring-boot-starter-parent + org.springframework.boot + 1.5.2.RELEASE + + + + com.baeldung + jhipster-monolithic + 0.0.1-SNAPSHOT + war + JHipster Monolithic Application + + + ${maven.version} + + + + + com.fasterxml.jackson.datatype + jackson-datatype-hibernate5 + + + com.fasterxml.jackson.datatype + jackson-datatype-hppc + + + com.fasterxml.jackson.datatype + jackson-datatype-json-org + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + com.h2database + h2 + + + com.jayway.jsonpath + json-path + test + + + + com.jcraft + jzlib + ${jzlib.version} + + + com.mattbertolini + liquibase-slf4j + ${liquibase-slf4j.version} + + + com.ryantenney.metrics + metrics-spring + ${metrics-spring.version} + + + metrics-annotation + com.codahale.metrics + + + metrics-core + com.codahale.metrics + + + metrics-healthchecks + com.codahale.metrics + + + + + com.zaxxer + HikariCP + + + tools + com.sun + + + + + + commons-io + commons-io + ${commons-io.version} + + + io.dropwizard.metrics + metrics-annotation + ${dropwizard-metrics.version} + + + io.dropwizard.metrics + metrics-core + + + io.dropwizard.metrics + metrics-json + ${dropwizard-metrics.version} + + + io.dropwizard.metrics + metrics-jvm + ${dropwizard-metrics.version} + + + io.dropwizard.metrics + metrics-servlet + ${dropwizard-metrics.version} + + + io.dropwizard.metrics + metrics-servlets + + + io.gatling.highcharts + gatling-charts-highcharts + ${gatling.version} + test + + + io.github.jhipster + jhipster + ${jhipster.server.version} + + + io.jsonwebtoken + jjwt + ${jjwt.version} + + + io.springfox + springfox-bean-validators + ${springfox.version} + + + io.springfox + springfox-swagger2 + ${springfox.version} + + + mapstruct + org.mapstruct + + + + + javax.cache + cache-api + + + mysql + mysql-connector-java + + + + net.logstash.logback + logstash-logback-encoder + ${logstash-logback-encoder.version} + + + logback-core + ch.qos.logback + + + logback-classic + ch.qos.logback + + + logback-access + ch.qos.logback + + + + + org.apache.commons + commons-lang3 + ${commons-lang.version} + + + org.assertj + assertj-core + test + + + org.awaitility + awaitility + ${awaitility.version} + test + + + org.ehcache + ehcache + + + org.hibernate + hibernate-envers + + + org.hibernate + hibernate-jcache + ${hibernate.version} + + + org.hibernate + hibernate-validator + + + org.liquibase + liquibase-core + + + jetty-servlet + org.eclipse.jetty + + + + + org.mapstruct + mapstruct-jdk8 + ${mapstruct.version} + + + org.springframework + spring-context-support + + + org.springframework.boot + spring-boot-actuator + + + org.springframework.boot + spring-boot-autoconfigure + + + org.springframework.boot + spring-boot-configuration-processor + true + + + org.springframework.boot + spring-boot-loader-tools + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.boot + spring-boot-starter-cloud-connectors + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-logging + + + org.springframework.boot + spring-boot-starter-mail + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-web + + + spring-boot-starter-tomcat + org.springframework.boot + + + + + org.springframework.boot + spring-boot-test + test + + + + org.springframework.security + spring-security-data + + + org.springframework.security + spring-security-test + test + + + + + spring-boot:run + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + org.jacoco + jacoco-maven-plugin + ${jacoco-maven-plugin.version} + + prepare-agent + + + + + + + + + com.github.eirslett + frontend-maven-plugin + ${frontend-maven-plugin.version} + + install-node-and-npm + npm + + + + + + + + + + + + + + + com.github.ekryd.sortpom + sortpom-maven-plugin + ${sortpom-maven-plugin.version} + + + verify + + sort + + + + + true + 4 + groupId,artifactId + groupId,artifactId + true + false + + + + com.spotify + docker-maven-plugin + ${docker-maven-plugin.version} + + baeldung + src/main/docker + + + / + ${project.build.directory} + ${project.build.finalName}.war + + + + + + io.gatling + gatling-maven-plugin + ${gatling-maven-plugin.version} + + src/test/gatling/conf + src/test/gatling/data + target/gatling/results + src/test/gatling/bodies + src/test/gatling/simulations + + true + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + 1.8 + 1.8 + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + + + + + + org.apache.maven.plugins + maven-eclipse-plugin + + true + true + + + + org.apache.maven.plugins + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-versions + + enforce + + + + + + + You are running an older version of + Maven. JHipster requires at least Maven + ${maven.version} + [${maven.version},) + + + You are running an older version of + Java. JHipster requires at least JDK + ${java.version} + [${java.version}.0,) + + + + + + org.apache.maven.plugins + maven-resources-plugin + ${maven-resources-plugin.version} + + + default-resources + validate + + copy-resources + + + target/classes + false + + # + + + + src/main/resources/ + true + + **/*.xml + **/*.yml + + + + src/main/resources/ + false + + **/*.xml + **/*.yml + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + alphabetical + + **/*IntTest.java + + + + + + org.jacoco + jacoco-maven-plugin + ${jacoco-maven-plugin.version} + + + pre-unit-tests + + prepare-agent + + + + ${project.testresult.directory}/coverage/jacoco/jacoco.exec + + + + + post-unit-test + test + + report + + + ${project.testresult.directory}/coverage/jacoco/jacoco.exec + ${project.testresult.directory}/coverage/jacoco + + + + + + org.liquibase + liquibase-maven-plugin + ${liquibase.version} + + + javax.validation + validation-api + ${validation-api.version} + + + org.javassist + javassist + ${javassist.version} + + + org.liquibase.ext + liquibase-hibernate5 + ${liquibase-hibernate5.version} + + + org.springframework.boot + spring-boot-starter-data-jpa + ${project.parent.version} + + + + src/main/resources/config/liquibase/master.xml + src/main/resources/config/liquibase/changelog/${maven.build.timestamp}_changelog.xml + org.h2.Driver + jdbc:h2:file:./target/h2db/db/baeldung + + baeldung + + hibernate:spring:com.baeldung.domain?dialect=org.hibernate.dialect.H2Dialect&hibernate.physical_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy&hibernate.implicit_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy + true + debug + + + + org.sonarsource.scanner.maven + sonar-maven-plugin + ${sonar-maven-plugin.version} + + + org.springframework.boot + spring-boot-maven-plugin + + true + true + + + + + + + + + no-liquibase + + ,no-liquibase + + + + swagger + + ,swagger + + + + webpack + + + + com.github.eirslett + frontend-maven-plugin + ${frontend-maven-plugin.version} + + + install node and npm + + install-node-and-npm + + + ${node.version} + ${npm.version} + + + + webpack build dev + generate-resources + + npm + + + run webpack:build:dev + + + + + + + + + dev + + true + + + + + org.apache.maven.plugins + maven-war-plugin + + src/main/webapp/ + + + + + + + DEBUG + + dev${profile.no-liquibase} + + + + org.springframework.boot + spring-boot-devtools + true + + + org.springframework.boot + spring-boot-starter-undertow + + + + + prod + + + + com.github.eirslett + frontend-maven-plugin + ${frontend-maven-plugin.version} + + + install node and npm + + install-node-and-npm + + + ${node.version} + ${npm.version} + + + + npm install + + npm + + + install + + + + npm rebuild node-sass + + npm + + + rebuild node-sass + + + + webpack build prod + generate-resources + + npm + + + run webpack:prod + + + + + + maven-clean-plugin + + + + target/www/ + + + + + + org.apache.maven.plugins + maven-war-plugin + + target/www/ + + + + org.springframework.boot + spring-boot-maven-plugin + + + + build-info + + + + + true + + + + + + + INFO + + prod${profile.swagger}${profile.no-liquibase} + + + + org.springframework.boot + spring-boot-starter-undertow + + + + + + cc + + + + net.alchim31.maven + scala-maven-plugin + ${scala-maven-plugin.version} + + + compile + compile + + add-source + compile + + + + test-compile + test-compile + + add-source + testCompile + + + + + incremental + true + ${scala.version} + + + + org.apache.maven.plugins + maven-compiler-plugin + + + default-compile + none + + + default-testCompile + none + + + + + org.apache.maven.plugins + maven-war-plugin + + src/main/webapp/ + + + + org.springframework.boot + spring-boot-maven-plugin + + true + true + true + + + + + + + + DEBUG + + dev,swagger + + + + org.springframework.boot + spring-boot-devtools + true + + + org.springframework.boot + spring-boot-starter-undertow + + + + + + graphite + + + io.dropwizard.metrics + metrics-graphite + + + + + + prometheus + + + io.prometheus + simpleclient + ${prometheus-simpleclient.version} + + + io.prometheus + simpleclient_dropwizard + ${prometheus-simpleclient.version} + + + io.prometheus + simpleclient_servlet + ${prometheus-simpleclient.version} + + + + + + IDE + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + + + + + integration + + true + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + integration-test + + test + + + + **/*IntTest.java + + + + + + + + + + + + -Djava.security.egd=file:/dev/./urandom -Xmx256m + 3.6.2 + 2.0.0 + 2.5 + 3.5 + 0.4.13 + 1.3 + 2.2.1 + 2.2.3 + 5.2.8.Final + 2.6.0 + 0.7.9 + 1.8 + 3.21.0-GA + 1.0.0 + 1.1.0 + 0.7.0 + 1.1.3 + 3.6 + 2.0.0 + 4.8 + jdt_apt + 1.1.0.Final + 3.6.0 + 1.4.1 + 3.0.1 + yyyyMMddHHmmss + ${java.version} + ${java.version} + 3.0.0 + 3.1.3 + v6.10.0 + 4.3.0 + + + + + ${project.build.directory}/test-results + 0.0.20 + false + 3.2.2 + 2.12.1 + 3.2 + + src/main/webapp/content/**/*.*, + src/main/webapp/bower_components/**/*.*, + src/main/webapp/i18n/*.js, target/www/**/*.* + + S3437,UndocumentedApi,BoldAndItalicTagsCheck + + + src/main/webapp/app/**/*.* + Web:BoldAndItalicTagsCheck + + src/main/java/**/* + squid:S3437 + + src/main/java/**/* + squid:UndocumentedApi + + ${project.testresult.directory}/coverage/jacoco/jacoco-it.exec + ${project.testresult.directory}/coverage/jacoco/jacoco.exec + jacoco + + ${project.testresult.directory}/karma + + ${project.testresult.directory}/coverage/report-lcov/lcov.info + + ${project.testresult.directory}/coverage/report-lcov/lcov.info + + ${project.basedir}/src/main/ + ${project.testresult.directory}/surefire-reports + ${project.basedir}/src/test/ + + 2.5.0 + + 2.6.1 + 1.4.10.Final + 1.1.0.Final + + diff --git a/jhipster/postcss.config.js b/jhipster/postcss.config.js new file mode 100644 index 000000000000..f549c034d589 --- /dev/null +++ b/jhipster/postcss.config.js @@ -0,0 +1,3 @@ +module.exports = { + plugins: [] +} diff --git a/jhipster/src/main/docker/Dockerfile b/jhipster/src/main/docker/Dockerfile new file mode 100644 index 000000000000..66991b2998d4 --- /dev/null +++ b/jhipster/src/main/docker/Dockerfile @@ -0,0 +1,13 @@ +FROM openjdk:8-jre-alpine + +ENV SPRING_OUTPUT_ANSI_ENABLED=ALWAYS \ + JHIPSTER_SLEEP=0 + +# add directly the war +ADD *.war /app.war + +VOLUME /tmp +EXPOSE 8080 +CMD echo "The application will start in ${JHIPSTER_SLEEP}s..." && \ + sleep ${JHIPSTER_SLEEP} && \ + java -Djava.security.egd=file:/dev/./urandom -jar /app.war diff --git a/jhipster/src/main/docker/app.yml b/jhipster/src/main/docker/app.yml new file mode 100644 index 000000000000..78cd2c1602c6 --- /dev/null +++ b/jhipster/src/main/docker/app.yml @@ -0,0 +1,14 @@ +version: '2' +services: + baeldung-app: + image: baeldung + environment: + - SPRING_PROFILES_ACTIVE=prod,swagger + - SPRING_DATASOURCE_URL=jdbc:mysql://baeldung-mysql:3306/baeldung?useUnicode=true&characterEncoding=utf8&useSSL=false + - JHIPSTER_SLEEP=10 # gives time for the database to boot before the application + ports: + - 8080:8080 + baeldung-mysql: + extends: + file: mysql.yml + service: baeldung-mysql diff --git a/jhipster/src/main/docker/mysql.yml b/jhipster/src/main/docker/mysql.yml new file mode 100644 index 000000000000..5038d095041c --- /dev/null +++ b/jhipster/src/main/docker/mysql.yml @@ -0,0 +1,13 @@ +version: '2' +services: + baeldung-mysql: + image: mysql:5.7.13 + # volumes: + # - ~/volumes/jhipster/baeldung/mysql/:/var/lib/mysql/ + environment: + - MYSQL_USER=root + - MYSQL_ALLOW_EMPTY_PASSWORD=yes + - MYSQL_DATABASE=baeldung + ports: + - 3306:3306 + command: mysqld --lower_case_table_names=1 --skip-ssl --character_set_server=utf8 diff --git a/jhipster/src/main/docker/sonar.yml b/jhipster/src/main/docker/sonar.yml new file mode 100644 index 000000000000..718be83b4d53 --- /dev/null +++ b/jhipster/src/main/docker/sonar.yml @@ -0,0 +1,7 @@ +version: '2' +services: + baeldung-sonar: + image: sonarqube:6.2-alpine + ports: + - 9000:9000 + - 9092:9092 diff --git a/jhipster/src/main/java/com/baeldung/ApplicationWebXml.java b/jhipster/src/main/java/com/baeldung/ApplicationWebXml.java new file mode 100644 index 000000000000..762d18420c7f --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/ApplicationWebXml.java @@ -0,0 +1,21 @@ +package com.baeldung; + +import com.baeldung.config.DefaultProfileUtil; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.support.SpringBootServletInitializer; + +/** + * This is a helper Java class that provides an alternative to creating a web.xml. + * This will be invoked only when the application is deployed to a servlet container like Tomcat, JBoss etc. + */ +public class ApplicationWebXml extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + /** + * set a default to use when no profile is configured. + */ + DefaultProfileUtil.addDefaultProfile(application.application()); + return application.sources(BaeldungApp.class); + } +} diff --git a/jhipster/src/main/java/com/baeldung/BaeldungApp.java b/jhipster/src/main/java/com/baeldung/BaeldungApp.java new file mode 100644 index 000000000000..13b6b2b3fa9a --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/BaeldungApp.java @@ -0,0 +1,84 @@ +package com.baeldung; + +import com.baeldung.config.ApplicationProperties; +import com.baeldung.config.DefaultProfileUtil; + +import io.github.jhipster.config.JHipsterConstants; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.actuate.autoconfigure.*; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.core.env.Environment; + +import javax.annotation.PostConstruct; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.Collection; + +@ComponentScan +@EnableAutoConfiguration(exclude = {MetricFilterAutoConfiguration.class, MetricRepositoryAutoConfiguration.class}) +@EnableConfigurationProperties({LiquibaseProperties.class, ApplicationProperties.class}) +public class BaeldungApp { + + private static final Logger log = LoggerFactory.getLogger(BaeldungApp.class); + + private final Environment env; + + public BaeldungApp(Environment env) { + this.env = env; + } + + /** + * Initializes baeldung. + *

+ * Spring profiles can be configured with a program arguments --spring.profiles.active=your-active-profile + *

+ * You can find more information on how profiles work with JHipster on http://jhipster.github.io/profiles/. + */ + @PostConstruct + public void initApplication() { + Collection activeProfiles = Arrays.asList(env.getActiveProfiles()); + if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) && activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_PRODUCTION)) { + log.error("You have misconfigured your application! It should not run " + + "with both the 'dev' and 'prod' profiles at the same time."); + } + if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) && activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_CLOUD)) { + log.error("You have misconfigured your application! It should not" + + "run with both the 'dev' and 'cloud' profiles at the same time."); + } + } + + /** + * Main method, used to run the application. + * + * @param args the command line arguments + * @throws UnknownHostException if the local host name could not be resolved into an address + */ + public static void main(String[] args) throws UnknownHostException { + SpringApplication app = new SpringApplication(BaeldungApp.class); + DefaultProfileUtil.addDefaultProfile(app); + Environment env = app.run(args).getEnvironment(); + String protocol = "http"; + if (env.getProperty("server.ssl.key-store") != null) { + protocol = "https"; + } + log.info("\n----------------------------------------------------------\n\t" + + "Application '{}' is running! Access URLs:\n\t" + + "Local: \t\t{}://localhost:{}\n\t" + + "External: \t{}://{}:{}\n\t" + + "Profile(s): \t{}\n----------------------------------------------------------", + env.getProperty("spring.application.name"), + protocol, + env.getProperty("server.port"), + protocol, + InetAddress.getLocalHost().getHostAddress(), + env.getProperty("server.port"), + env.getActiveProfiles()); + } +} diff --git a/jhipster/src/main/java/com/baeldung/aop/logging/LoggingAspect.java b/jhipster/src/main/java/com/baeldung/aop/logging/LoggingAspect.java new file mode 100644 index 000000000000..7fbd3528201b --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/aop/logging/LoggingAspect.java @@ -0,0 +1,79 @@ +package com.baeldung.aop.logging; + +import io.github.jhipster.config.JHipsterConstants; + +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; + +import java.util.Arrays; + +/** + * Aspect for logging execution of service and repository Spring components. + * + * By default, it only runs with the "dev" profile. + */ +@Aspect +public class LoggingAspect { + + private final Logger log = LoggerFactory.getLogger(this.getClass()); + + private final Environment env; + + public LoggingAspect(Environment env) { + this.env = env; + } + + /** + * Pointcut that matches all repositories, services and Web REST endpoints. + */ + @Pointcut("within(com.baeldung.repository..*) || within(com.baeldung.service..*) || within(com.baeldung.web.rest..*)") + public void loggingPointcut() { + // Method is empty as this is just a Pointcut, the implementations are in the advices. + } + + /** + * Advice that logs methods throwing exceptions. + */ + @AfterThrowing(pointcut = "loggingPointcut()", throwing = "e") + public void logAfterThrowing(JoinPoint joinPoint, Throwable e) { + if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)) { + log.error("Exception in {}.{}() with cause = \'{}\' and exception = \'{}\'", joinPoint.getSignature().getDeclaringTypeName(), + joinPoint.getSignature().getName(), e.getCause() != null? e.getCause() : "NULL", e.getMessage(), e); + + } else { + log.error("Exception in {}.{}() with cause = {}", joinPoint.getSignature().getDeclaringTypeName(), + joinPoint.getSignature().getName(), e.getCause() != null? e.getCause() : "NULL"); + } + } + + /** + * Advice that logs when a method is entered and exited. + */ + @Around("loggingPointcut()") + public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { + if (log.isDebugEnabled()) { + log.debug("Enter: {}.{}() with argument[s] = {}", joinPoint.getSignature().getDeclaringTypeName(), + joinPoint.getSignature().getName(), Arrays.toString(joinPoint.getArgs())); + } + try { + Object result = joinPoint.proceed(); + if (log.isDebugEnabled()) { + log.debug("Exit: {}.{}() with result = {}", joinPoint.getSignature().getDeclaringTypeName(), + joinPoint.getSignature().getName(), result); + } + return result; + } catch (IllegalArgumentException e) { + log.error("Illegal argument: {} in {}.{}()", Arrays.toString(joinPoint.getArgs()), + joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName()); + + throw e; + } + } +} diff --git a/jhipster/src/main/java/com/baeldung/config/ApplicationProperties.java b/jhipster/src/main/java/com/baeldung/config/ApplicationProperties.java new file mode 100644 index 000000000000..add1782678a1 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/ApplicationProperties.java @@ -0,0 +1,15 @@ +package com.baeldung.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Properties specific to JHipster. + * + *

+ * Properties are configured in the application.yml file. + *

+ */ +@ConfigurationProperties(prefix = "application", ignoreUnknownFields = false) +public class ApplicationProperties { + +} diff --git a/jhipster/src/main/java/com/baeldung/config/AsyncConfiguration.java b/jhipster/src/main/java/com/baeldung/config/AsyncConfiguration.java new file mode 100644 index 000000000000..dc1ba3751404 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/AsyncConfiguration.java @@ -0,0 +1,46 @@ +package com.baeldung.config; + +import io.github.jhipster.async.ExceptionHandlingAsyncTaskExecutor; +import io.github.jhipster.config.JHipsterProperties; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; +import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.*; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.Executor; + +@Configuration +@EnableAsync +@EnableScheduling +public class AsyncConfiguration implements AsyncConfigurer { + + private final Logger log = LoggerFactory.getLogger(AsyncConfiguration.class); + + private final JHipsterProperties jHipsterProperties; + + public AsyncConfiguration(JHipsterProperties jHipsterProperties) { + this.jHipsterProperties = jHipsterProperties; + } + + @Override + @Bean(name = "taskExecutor") + public Executor getAsyncExecutor() { + log.debug("Creating Async Task Executor"); + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(jHipsterProperties.getAsync().getCorePoolSize()); + executor.setMaxPoolSize(jHipsterProperties.getAsync().getMaxPoolSize()); + executor.setQueueCapacity(jHipsterProperties.getAsync().getQueueCapacity()); + executor.setThreadNamePrefix("baeldung-Executor-"); + return new ExceptionHandlingAsyncTaskExecutor(executor); + } + + @Override + public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { + return new SimpleAsyncUncaughtExceptionHandler(); + } +} diff --git a/jhipster/src/main/java/com/baeldung/config/CacheConfiguration.java b/jhipster/src/main/java/com/baeldung/config/CacheConfiguration.java new file mode 100644 index 000000000000..4323fa076ab0 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/CacheConfiguration.java @@ -0,0 +1,48 @@ +package com.baeldung.config; + +import io.github.jhipster.config.JHipsterProperties; +import org.ehcache.config.builders.CacheConfigurationBuilder; +import org.ehcache.config.builders.ResourcePoolsBuilder; +import org.ehcache.expiry.Duration; +import org.ehcache.expiry.Expirations; +import org.ehcache.jsr107.Eh107Configuration; + +import java.util.concurrent.TimeUnit; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.*; + +@Configuration +@EnableCaching +@AutoConfigureAfter(value = { MetricsConfiguration.class }) +@AutoConfigureBefore(value = { WebConfigurer.class, DatabaseConfiguration.class }) +public class CacheConfiguration { + + private final javax.cache.configuration.Configuration jcacheConfiguration; + + public CacheConfiguration(JHipsterProperties jHipsterProperties) { + JHipsterProperties.Cache.Ehcache ehcache = + jHipsterProperties.getCache().getEhcache(); + + jcacheConfiguration = Eh107Configuration.fromEhcacheCacheConfiguration( + CacheConfigurationBuilder.newCacheConfigurationBuilder(Object.class, Object.class, + ResourcePoolsBuilder.heap(ehcache.getMaxEntries())) + .withExpiry(Expirations.timeToLiveExpiration(Duration.of(ehcache.getTimeToLiveSeconds(), TimeUnit.SECONDS))) + .build()); + } + + @Bean + public JCacheManagerCustomizer cacheManagerCustomizer() { + return cm -> { + cm.createCache(com.baeldung.domain.User.class.getName(), jcacheConfiguration); + cm.createCache(com.baeldung.domain.Authority.class.getName(), jcacheConfiguration); + cm.createCache(com.baeldung.domain.User.class.getName() + ".authorities", jcacheConfiguration); + cm.createCache(com.baeldung.domain.Post.class.getName(), jcacheConfiguration); + cm.createCache(com.baeldung.domain.Comment.class.getName(), jcacheConfiguration); + // jhipster-needle-ehcache-add-entry + }; + } +} diff --git a/jhipster/src/main/java/com/baeldung/config/CloudDatabaseConfiguration.java b/jhipster/src/main/java/com/baeldung/config/CloudDatabaseConfiguration.java new file mode 100644 index 000000000000..cae9ec1e8246 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/CloudDatabaseConfiguration.java @@ -0,0 +1,23 @@ +package com.baeldung.config; + +import io.github.jhipster.config.JHipsterConstants; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.config.java.AbstractCloudConfig; +import org.springframework.context.annotation.*; + +import javax.sql.DataSource; + +@Configuration +@Profile(JHipsterConstants.SPRING_PROFILE_CLOUD) +public class CloudDatabaseConfiguration extends AbstractCloudConfig { + + private final Logger log = LoggerFactory.getLogger(CloudDatabaseConfiguration.class); + + @Bean + public DataSource dataSource() { + log.info("Configuring JDBC datasource from a cloud provider"); + return connectionFactory().dataSource(); + } +} diff --git a/jhipster/src/main/java/com/baeldung/config/Constants.java b/jhipster/src/main/java/com/baeldung/config/Constants.java new file mode 100644 index 000000000000..9d797c19348a --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/Constants.java @@ -0,0 +1,16 @@ +package com.baeldung.config; + +/** + * Application constants. + */ +public final class Constants { + + //Regex for acceptable logins + public static final String LOGIN_REGEX = "^[_'.@A-Za-z0-9-]*$"; + + public static final String SYSTEM_ACCOUNT = "system"; + public static final String ANONYMOUS_USER = "anonymoususer"; + + private Constants() { + } +} diff --git a/jhipster/src/main/java/com/baeldung/config/DatabaseConfiguration.java b/jhipster/src/main/java/com/baeldung/config/DatabaseConfiguration.java new file mode 100644 index 000000000000..b64fd7cc8228 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/DatabaseConfiguration.java @@ -0,0 +1,75 @@ +package com.baeldung.config; + +import io.github.jhipster.config.JHipsterConstants; +import io.github.jhipster.config.liquibase.AsyncSpringLiquibase; + +import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module; +import liquibase.integration.spring.SpringLiquibase; +import org.h2.tools.Server; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.core.env.Environment; +import org.springframework.core.task.TaskExecutor; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +import javax.sql.DataSource; +import java.sql.SQLException; + +@Configuration +@EnableJpaRepositories("com.baeldung.repository") +@EnableJpaAuditing(auditorAwareRef = "springSecurityAuditorAware") +@EnableTransactionManagement +public class DatabaseConfiguration { + + private final Logger log = LoggerFactory.getLogger(DatabaseConfiguration.class); + + private final Environment env; + + public DatabaseConfiguration(Environment env) { + this.env = env; + } + + /** + * Open the TCP port for the H2 database, so it is available remotely. + * + * @return the H2 database TCP server + * @throws SQLException if the server failed to start + */ + @Bean(initMethod = "start", destroyMethod = "stop") + @Profile(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) + public Server h2TCPServer() throws SQLException { + return Server.createTcpServer("-tcp","-tcpAllowOthers"); + } + + @Bean + public SpringLiquibase liquibase(@Qualifier("taskExecutor") TaskExecutor taskExecutor, + DataSource dataSource, LiquibaseProperties liquibaseProperties) { + + // Use liquibase.integration.spring.SpringLiquibase if you don't want Liquibase to start asynchronously + SpringLiquibase liquibase = new AsyncSpringLiquibase(taskExecutor, env); + liquibase.setDataSource(dataSource); + liquibase.setChangeLog("classpath:config/liquibase/master.xml"); + liquibase.setContexts(liquibaseProperties.getContexts()); + liquibase.setDefaultSchema(liquibaseProperties.getDefaultSchema()); + liquibase.setDropFirst(liquibaseProperties.isDropFirst()); + if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_NO_LIQUIBASE)) { + liquibase.setShouldRun(false); + } else { + liquibase.setShouldRun(liquibaseProperties.isEnabled()); + log.debug("Configuring Liquibase"); + } + return liquibase; + } + + @Bean + public Hibernate5Module hibernate5Module() { + return new Hibernate5Module(); + } +} diff --git a/jhipster/src/main/java/com/baeldung/config/DateTimeFormatConfiguration.java b/jhipster/src/main/java/com/baeldung/config/DateTimeFormatConfiguration.java new file mode 100644 index 000000000000..aab57adfb039 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/DateTimeFormatConfiguration.java @@ -0,0 +1,17 @@ +package com.baeldung.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.format.FormatterRegistry; +import org.springframework.format.datetime.standard.DateTimeFormatterRegistrar; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; + +@Configuration +public class DateTimeFormatConfiguration extends WebMvcConfigurerAdapter { + + @Override + public void addFormatters(FormatterRegistry registry) { + DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar(); + registrar.setUseIsoFormat(true); + registrar.registerFormatters(registry); + } +} diff --git a/jhipster/src/main/java/com/baeldung/config/DefaultProfileUtil.java b/jhipster/src/main/java/com/baeldung/config/DefaultProfileUtil.java new file mode 100644 index 000000000000..7918fb49a403 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/DefaultProfileUtil.java @@ -0,0 +1,48 @@ +package com.baeldung.config; + +import io.github.jhipster.config.JHipsterConstants; + +import org.springframework.boot.SpringApplication; +import org.springframework.core.env.Environment; + +import java.util.*; + +/** + * Utility class to load a Spring profile to be used as default + * when there is no spring.profiles.active set in the environment or as command line argument. + * If the value is not available in application.yml then dev profile will be used as default. + */ +public final class DefaultProfileUtil { + + private static final String SPRING_PROFILE_DEFAULT = "spring.profiles.default"; + + private DefaultProfileUtil() { + } + + /** + * Set a default to use when no profile is configured. + * + * @param app the Spring application + */ + public static void addDefaultProfile(SpringApplication app) { + Map defProperties = new HashMap<>(); + /* + * The default profile to use when no other profiles are defined + * This cannot be set in the application.yml file. + * See https://github.com/spring-projects/spring-boot/issues/1219 + */ + defProperties.put(SPRING_PROFILE_DEFAULT, JHipsterConstants.SPRING_PROFILE_DEVELOPMENT); + app.setDefaultProperties(defProperties); + } + + /** + * Get the profiles that are applied else get default profiles. + */ + public static String[] getActiveProfiles(Environment env) { + String[] profiles = env.getActiveProfiles(); + if (profiles.length == 0) { + return env.getDefaultProfiles(); + } + return profiles; + } +} diff --git a/jhipster/src/main/java/com/baeldung/config/LocaleConfiguration.java b/jhipster/src/main/java/com/baeldung/config/LocaleConfiguration.java new file mode 100644 index 000000000000..dd3a499d5608 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/LocaleConfiguration.java @@ -0,0 +1,35 @@ +package com.baeldung.config; + +import io.github.jhipster.config.locale.AngularCookieLocaleResolver; + +import org.springframework.context.EnvironmentAware; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.web.servlet.LocaleResolver; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; +import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; + +@Configuration +public class LocaleConfiguration extends WebMvcConfigurerAdapter implements EnvironmentAware { + + @Override + public void setEnvironment(Environment environment) { + // unused + } + + @Bean(name = "localeResolver") + public LocaleResolver localeResolver() { + AngularCookieLocaleResolver cookieLocaleResolver = new AngularCookieLocaleResolver(); + cookieLocaleResolver.setCookieName("NG_TRANSLATE_LANG_KEY"); + return cookieLocaleResolver; + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor(); + localeChangeInterceptor.setParamName("language"); + registry.addInterceptor(localeChangeInterceptor); + } +} diff --git a/jhipster/src/main/java/com/baeldung/config/LoggingAspectConfiguration.java b/jhipster/src/main/java/com/baeldung/config/LoggingAspectConfiguration.java new file mode 100644 index 000000000000..936f54709af6 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/LoggingAspectConfiguration.java @@ -0,0 +1,19 @@ +package com.baeldung.config; + +import com.baeldung.aop.logging.LoggingAspect; + +import io.github.jhipster.config.JHipsterConstants; + +import org.springframework.context.annotation.*; +import org.springframework.core.env.Environment; + +@Configuration +@EnableAspectJAutoProxy +public class LoggingAspectConfiguration { + + @Bean + @Profile(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) + public LoggingAspect loggingAspect(Environment env) { + return new LoggingAspect(env); + } +} diff --git a/jhipster/src/main/java/com/baeldung/config/LoggingConfiguration.java b/jhipster/src/main/java/com/baeldung/config/LoggingConfiguration.java new file mode 100644 index 000000000000..3ca6a4882187 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/LoggingConfiguration.java @@ -0,0 +1,109 @@ +package com.baeldung.config; + +import io.github.jhipster.config.JHipsterProperties; + +import ch.qos.logback.classic.AsyncAppender; +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.LoggerContextListener; +import ch.qos.logback.core.spi.ContextAwareBase; +import net.logstash.logback.appender.LogstashSocketAppender; +import net.logstash.logback.stacktrace.ShortenedThrowableConverter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class LoggingConfiguration { + + private final Logger log = LoggerFactory.getLogger(LoggingConfiguration.class); + + private LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); + + @Value("${spring.application.name}") + private String appName; + + @Value("${server.port}") + private String serverPort; + + private final JHipsterProperties jHipsterProperties; + + public LoggingConfiguration(JHipsterProperties jHipsterProperties) { + this.jHipsterProperties = jHipsterProperties; + if (jHipsterProperties.getLogging().getLogstash().isEnabled()) { + addLogstashAppender(context); + + // Add context listener + LogbackLoggerContextListener loggerContextListener = new LogbackLoggerContextListener(); + loggerContextListener.setContext(context); + context.addListener(loggerContextListener); + } + } + + public void addLogstashAppender(LoggerContext context) { + log.info("Initializing Logstash logging"); + + LogstashSocketAppender logstashAppender = new LogstashSocketAppender(); + logstashAppender.setName("LOGSTASH"); + logstashAppender.setContext(context); + String customFields = "{\"app_name\":\"" + appName + "\",\"app_port\":\"" + serverPort + "\"}"; + + // Set the Logstash appender config from JHipster properties + logstashAppender.setSyslogHost(jHipsterProperties.getLogging().getLogstash().getHost()); + logstashAppender.setPort(jHipsterProperties.getLogging().getLogstash().getPort()); + logstashAppender.setCustomFields(customFields); + + // Limit the maximum length of the forwarded stacktrace so that it won't exceed the 8KB UDP limit of logstash + ShortenedThrowableConverter throwableConverter = new ShortenedThrowableConverter(); + throwableConverter.setMaxLength(7500); + throwableConverter.setRootCauseFirst(true); + logstashAppender.setThrowableConverter(throwableConverter); + + logstashAppender.start(); + + // Wrap the appender in an Async appender for performance + AsyncAppender asyncLogstashAppender = new AsyncAppender(); + asyncLogstashAppender.setContext(context); + asyncLogstashAppender.setName("ASYNC_LOGSTASH"); + asyncLogstashAppender.setQueueSize(jHipsterProperties.getLogging().getLogstash().getQueueSize()); + asyncLogstashAppender.addAppender(logstashAppender); + asyncLogstashAppender.start(); + + context.getLogger("ROOT").addAppender(asyncLogstashAppender); + } + + /** + * Logback configuration is achieved by configuration file and API. + * When configuration file change is detected, the configuration is reset. + * This listener ensures that the programmatic configuration is also re-applied after reset. + */ + class LogbackLoggerContextListener extends ContextAwareBase implements LoggerContextListener { + + @Override + public boolean isResetResistant() { + return true; + } + + @Override + public void onStart(LoggerContext context) { + addLogstashAppender(context); + } + + @Override + public void onReset(LoggerContext context) { + addLogstashAppender(context); + } + + @Override + public void onStop(LoggerContext context) { + // Nothing to do. + } + + @Override + public void onLevelChange(ch.qos.logback.classic.Logger logger, Level level) { + // Nothing to do. + } + } + +} diff --git a/jhipster/src/main/java/com/baeldung/config/MetricsConfiguration.java b/jhipster/src/main/java/com/baeldung/config/MetricsConfiguration.java new file mode 100644 index 000000000000..94a50bed91fb --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/MetricsConfiguration.java @@ -0,0 +1,94 @@ +package com.baeldung.config; + +import io.github.jhipster.config.JHipsterProperties; +import io.github.jhipster.config.jcache.JCacheGaugeSet; + +import com.codahale.metrics.JmxReporter; +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Slf4jReporter; +import com.codahale.metrics.health.HealthCheckRegistry; +import com.codahale.metrics.jvm.*; +import com.ryantenney.metrics.spring.config.annotation.EnableMetrics; +import com.ryantenney.metrics.spring.config.annotation.MetricsConfigurerAdapter; +import com.zaxxer.hikari.HikariDataSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.*; + +import javax.annotation.PostConstruct; +import java.lang.management.ManagementFactory; +import java.util.concurrent.TimeUnit; + +@Configuration +@EnableMetrics(proxyTargetClass = true) +public class MetricsConfiguration extends MetricsConfigurerAdapter { + + private static final String PROP_METRIC_REG_JVM_MEMORY = "jvm.memory"; + private static final String PROP_METRIC_REG_JVM_GARBAGE = "jvm.garbage"; + private static final String PROP_METRIC_REG_JVM_THREADS = "jvm.threads"; + private static final String PROP_METRIC_REG_JVM_FILES = "jvm.files"; + private static final String PROP_METRIC_REG_JVM_BUFFERS = "jvm.buffers"; + + private static final String PROP_METRIC_REG_JCACHE_STATISTICS = "jcache.statistics"; + private final Logger log = LoggerFactory.getLogger(MetricsConfiguration.class); + + private MetricRegistry metricRegistry = new MetricRegistry(); + + private HealthCheckRegistry healthCheckRegistry = new HealthCheckRegistry(); + + private final JHipsterProperties jHipsterProperties; + + private HikariDataSource hikariDataSource; + + public MetricsConfiguration(JHipsterProperties jHipsterProperties) { + this.jHipsterProperties = jHipsterProperties; + } + + @Autowired(required = false) + public void setHikariDataSource(HikariDataSource hikariDataSource) { + this.hikariDataSource = hikariDataSource; + } + + @Override + @Bean + public MetricRegistry getMetricRegistry() { + return metricRegistry; + } + + @Override + @Bean + public HealthCheckRegistry getHealthCheckRegistry() { + return healthCheckRegistry; + } + + @PostConstruct + public void init() { + log.debug("Registering JVM gauges"); + metricRegistry.register(PROP_METRIC_REG_JVM_MEMORY, new MemoryUsageGaugeSet()); + metricRegistry.register(PROP_METRIC_REG_JVM_GARBAGE, new GarbageCollectorMetricSet()); + metricRegistry.register(PROP_METRIC_REG_JVM_THREADS, new ThreadStatesGaugeSet()); + metricRegistry.register(PROP_METRIC_REG_JVM_FILES, new FileDescriptorRatioGauge()); + metricRegistry.register(PROP_METRIC_REG_JVM_BUFFERS, new BufferPoolMetricSet(ManagementFactory.getPlatformMBeanServer())); + + metricRegistry.register(PROP_METRIC_REG_JCACHE_STATISTICS, new JCacheGaugeSet()); + if (hikariDataSource != null) { + log.debug("Monitoring the datasource"); + hikariDataSource.setMetricRegistry(metricRegistry); + } + if (jHipsterProperties.getMetrics().getJmx().isEnabled()) { + log.debug("Initializing Metrics JMX reporting"); + JmxReporter jmxReporter = JmxReporter.forRegistry(metricRegistry).build(); + jmxReporter.start(); + } + if (jHipsterProperties.getMetrics().getLogs().isEnabled()) { + log.info("Initializing Metrics Log reporting"); + final Slf4jReporter reporter = Slf4jReporter.forRegistry(metricRegistry) + .outputTo(LoggerFactory.getLogger("metrics")) + .convertRatesTo(TimeUnit.SECONDS) + .convertDurationsTo(TimeUnit.MILLISECONDS) + .build(); + reporter.start(jHipsterProperties.getMetrics().getLogs().getReportFrequency(), TimeUnit.SECONDS); + } + } +} diff --git a/jhipster/src/main/java/com/baeldung/config/SecurityConfiguration.java b/jhipster/src/main/java/com/baeldung/config/SecurityConfiguration.java new file mode 100644 index 000000000000..118d302fcfe2 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/SecurityConfiguration.java @@ -0,0 +1,127 @@ +package com.baeldung.config; + +import com.baeldung.security.*; +import com.baeldung.security.jwt.*; + +import io.github.jhipster.security.*; + +import org.springframework.beans.factory.BeanInitializationException; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +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.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.data.repository.query.SecurityEvaluationContextExtension; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.filter.CorsFilter; + +import javax.annotation.PostConstruct; + +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) +public class SecurityConfiguration extends WebSecurityConfigurerAdapter { + + private final AuthenticationManagerBuilder authenticationManagerBuilder; + + private final UserDetailsService userDetailsService; + + private final TokenProvider tokenProvider; + + private final CorsFilter corsFilter; + + public SecurityConfiguration(AuthenticationManagerBuilder authenticationManagerBuilder, UserDetailsService userDetailsService, + TokenProvider tokenProvider, + CorsFilter corsFilter) { + + this.authenticationManagerBuilder = authenticationManagerBuilder; + this.userDetailsService = userDetailsService; + this.tokenProvider = tokenProvider; + this.corsFilter = corsFilter; + } + + @PostConstruct + public void init() { + try { + authenticationManagerBuilder + .userDetailsService(userDetailsService) + .passwordEncoder(passwordEncoder()); + } catch (Exception e) { + throw new BeanInitializationException("Security configuration failed", e); + } + } + + @Bean + public Http401UnauthorizedEntryPoint http401UnauthorizedEntryPoint() { + return new Http401UnauthorizedEntryPoint(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Override + public void configure(WebSecurity web) throws Exception { + web.ignoring() + .antMatchers(HttpMethod.OPTIONS, "/**") + .antMatchers("/app/**/*.{js,html}") + .antMatchers("/bower_components/**") + .antMatchers("/i18n/**") + .antMatchers("/content/**") + .antMatchers("/swagger-ui/index.html") + .antMatchers("/test/**") + .antMatchers("/h2-console/**"); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class) + .exceptionHandling() + .authenticationEntryPoint(http401UnauthorizedEntryPoint()) + .and() + .csrf() + .disable() + .headers() + .frameOptions() + .disable() + .and() + .sessionManagement() + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .and() + .authorizeRequests() + .antMatchers("/api/register").permitAll() + .antMatchers("/api/activate").permitAll() + .antMatchers("/api/authenticate").permitAll() + .antMatchers("/api/account/reset_password/init").permitAll() + .antMatchers("/api/account/reset_password/finish").permitAll() + .antMatchers("/api/profile-info").permitAll() + .antMatchers("/api/**").authenticated() + .antMatchers("/management/health").permitAll() + .antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN) + .antMatchers("/v2/api-docs/**").permitAll() + .antMatchers("/swagger-resources/configuration/ui").permitAll() + .antMatchers("/swagger-ui/index.html").hasAuthority(AuthoritiesConstants.ADMIN) + .and() + .apply(securityConfigurerAdapter()); + + } + + private JWTConfigurer securityConfigurerAdapter() { + return new JWTConfigurer(tokenProvider); + } + + @Bean + public SecurityEvaluationContextExtension securityEvaluationContextExtension() { + return new SecurityEvaluationContextExtension(); + } +} diff --git a/jhipster/src/main/java/com/baeldung/config/ThymeleafConfiguration.java b/jhipster/src/main/java/com/baeldung/config/ThymeleafConfiguration.java new file mode 100644 index 000000000000..68afcae71a2d --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/ThymeleafConfiguration.java @@ -0,0 +1,26 @@ +package com.baeldung.config; + +import org.apache.commons.lang3.CharEncoding; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.*; +import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver; + +@Configuration +public class ThymeleafConfiguration { + + @SuppressWarnings("unused") + private final Logger log = LoggerFactory.getLogger(ThymeleafConfiguration.class); + + @Bean + @Description("Thymeleaf template resolver serving HTML 5 emails") + public ClassLoaderTemplateResolver emailTemplateResolver() { + ClassLoaderTemplateResolver emailTemplateResolver = new ClassLoaderTemplateResolver(); + emailTemplateResolver.setPrefix("mails/"); + emailTemplateResolver.setSuffix(".html"); + emailTemplateResolver.setTemplateMode("HTML5"); + emailTemplateResolver.setCharacterEncoding(CharEncoding.UTF_8); + emailTemplateResolver.setOrder(1); + return emailTemplateResolver; + } +} diff --git a/jhipster/src/main/java/com/baeldung/config/WebConfigurer.java b/jhipster/src/main/java/com/baeldung/config/WebConfigurer.java new file mode 100644 index 000000000000..bd80a063eb04 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/WebConfigurer.java @@ -0,0 +1,186 @@ +package com.baeldung.config; + +import io.github.jhipster.config.JHipsterConstants; +import io.github.jhipster.config.JHipsterProperties; +import io.github.jhipster.web.filter.CachingHttpHeadersFilter; + +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.servlet.InstrumentedFilter; +import com.codahale.metrics.servlets.MetricsServlet; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.embedded.*; +import org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory; +import io.undertow.UndertowOptions; +import org.springframework.boot.web.servlet.ServletContextInitializer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; + +import java.io.File; +import java.nio.file.Paths; +import java.util.*; +import javax.servlet.*; + +/** + * Configuration of web application with Servlet 3.0 APIs. + */ +@Configuration +public class WebConfigurer implements ServletContextInitializer, EmbeddedServletContainerCustomizer { + + private final Logger log = LoggerFactory.getLogger(WebConfigurer.class); + + private final Environment env; + + private final JHipsterProperties jHipsterProperties; + + private MetricRegistry metricRegistry; + + public WebConfigurer(Environment env, JHipsterProperties jHipsterProperties) { + + this.env = env; + this.jHipsterProperties = jHipsterProperties; + } + + @Override + public void onStartup(ServletContext servletContext) throws ServletException { + if (env.getActiveProfiles().length != 0) { + log.info("Web application configuration, using profiles: {}", (Object[]) env.getActiveProfiles()); + } + EnumSet disps = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ASYNC); + initMetrics(servletContext, disps); + if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_PRODUCTION)) { + initCachingHttpHeadersFilter(servletContext, disps); + } + if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)) { + initH2Console(servletContext); + } + log.info("Web application fully configured"); + } + + /** + * Customize the Servlet engine: Mime types, the document root, the cache. + */ + @Override + public void customize(ConfigurableEmbeddedServletContainer container) { + MimeMappings mappings = new MimeMappings(MimeMappings.DEFAULT); + // IE issue, see https://github.com/jhipster/generator-jhipster/pull/711 + mappings.add("html", "text/html;charset=utf-8"); + // CloudFoundry issue, see https://github.com/cloudfoundry/gorouter/issues/64 + mappings.add("json", "text/html;charset=utf-8"); + container.setMimeMappings(mappings); + // When running in an IDE or with ./mvnw spring-boot:run, set location of the static web assets. + setLocationForStaticAssets(container); + + /* + * Enable HTTP/2 for Undertow - https://twitter.com/ankinson/status/829256167700492288 + * HTTP/2 requires HTTPS, so HTTP requests will fallback to HTTP/1.1. + * See the JHipsterProperties class and your application-*.yml configuration files + * for more information. + */ + if (jHipsterProperties.getHttp().getVersion().equals(JHipsterProperties.Http.Version.V_2_0) && + container instanceof UndertowEmbeddedServletContainerFactory) { + + ((UndertowEmbeddedServletContainerFactory) container) + .addBuilderCustomizers(builder -> + builder.setServerOption(UndertowOptions.ENABLE_HTTP2, true)); + } + } + + private void setLocationForStaticAssets(ConfigurableEmbeddedServletContainer container) { + File root; + String prefixPath = resolvePathPrefix(); + root = new File(prefixPath + "target/www/"); + if (root.exists() && root.isDirectory()) { + container.setDocumentRoot(root); + } + } + + /** + * Resolve path prefix to static resources. + */ + private String resolvePathPrefix() { + String fullExecutablePath = this.getClass().getResource("").getPath(); + String rootPath = Paths.get(".").toUri().normalize().getPath(); + String extractedPath = fullExecutablePath.replace(rootPath, ""); + int extractionEndIndex = extractedPath.indexOf("target/"); + if(extractionEndIndex <= 0) { + return ""; + } + return extractedPath.substring(0, extractionEndIndex); + } + + /** + * Initializes the caching HTTP Headers Filter. + */ + private void initCachingHttpHeadersFilter(ServletContext servletContext, + EnumSet disps) { + log.debug("Registering Caching HTTP Headers Filter"); + FilterRegistration.Dynamic cachingHttpHeadersFilter = + servletContext.addFilter("cachingHttpHeadersFilter", + new CachingHttpHeadersFilter(jHipsterProperties)); + + cachingHttpHeadersFilter.addMappingForUrlPatterns(disps, true, "/content/*"); + cachingHttpHeadersFilter.addMappingForUrlPatterns(disps, true, "/app/*"); + cachingHttpHeadersFilter.setAsyncSupported(true); + } + + /** + * Initializes Metrics. + */ + private void initMetrics(ServletContext servletContext, EnumSet disps) { + log.debug("Initializing Metrics registries"); + servletContext.setAttribute(InstrumentedFilter.REGISTRY_ATTRIBUTE, + metricRegistry); + servletContext.setAttribute(MetricsServlet.METRICS_REGISTRY, + metricRegistry); + + log.debug("Registering Metrics Filter"); + FilterRegistration.Dynamic metricsFilter = servletContext.addFilter("webappMetricsFilter", + new InstrumentedFilter()); + + metricsFilter.addMappingForUrlPatterns(disps, true, "/*"); + metricsFilter.setAsyncSupported(true); + + log.debug("Registering Metrics Servlet"); + ServletRegistration.Dynamic metricsAdminServlet = + servletContext.addServlet("metricsServlet", new MetricsServlet()); + + metricsAdminServlet.addMapping("/management/metrics/*"); + metricsAdminServlet.setAsyncSupported(true); + metricsAdminServlet.setLoadOnStartup(2); + } + + @Bean + public CorsFilter corsFilter() { + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + CorsConfiguration config = jHipsterProperties.getCors(); + if (config.getAllowedOrigins() != null && !config.getAllowedOrigins().isEmpty()) { + log.debug("Registering CORS filter"); + source.registerCorsConfiguration("/api/**", config); + source.registerCorsConfiguration("/v2/api-docs", config); + } + return new CorsFilter(source); + } + + /** + * Initializes H2 console. + */ + private void initH2Console(ServletContext servletContext) { + log.debug("Initialize H2 console"); + ServletRegistration.Dynamic h2ConsoleServlet = servletContext.addServlet("H2Console", new org.h2.server.web.WebServlet()); + h2ConsoleServlet.addMapping("/h2-console/*"); + h2ConsoleServlet.setInitParameter("-properties", "src/main/resources/"); + h2ConsoleServlet.setLoadOnStartup(1); + } + + @Autowired(required = false) + public void setMetricRegistry(MetricRegistry metricRegistry) { + this.metricRegistry = metricRegistry; + } +} diff --git a/jhipster/src/main/java/com/baeldung/config/audit/AuditEventConverter.java b/jhipster/src/main/java/com/baeldung/config/audit/AuditEventConverter.java new file mode 100644 index 000000000000..206232075184 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/audit/AuditEventConverter.java @@ -0,0 +1,91 @@ +package com.baeldung.config.audit; + +import com.baeldung.domain.PersistentAuditEvent; + +import org.springframework.boot.actuate.audit.AuditEvent; +import org.springframework.security.web.authentication.WebAuthenticationDetails; +import org.springframework.stereotype.Component; + +import java.time.Instant; +import java.time.ZoneId; +import java.util.*; + +@Component +public class AuditEventConverter { + + /** + * Convert a list of PersistentAuditEvent to a list of AuditEvent + * + * @param persistentAuditEvents the list to convert + * @return the converted list. + */ + public List convertToAuditEvent(Iterable persistentAuditEvents) { + if (persistentAuditEvents == null) { + return Collections.emptyList(); + } + List auditEvents = new ArrayList<>(); + for (PersistentAuditEvent persistentAuditEvent : persistentAuditEvents) { + auditEvents.add(convertToAuditEvent(persistentAuditEvent)); + } + return auditEvents; + } + + /** + * Convert a PersistentAuditEvent to an AuditEvent + * + * @param persistentAuditEvent the event to convert + * @return the converted list. + */ + public AuditEvent convertToAuditEvent(PersistentAuditEvent persistentAuditEvent) { + Instant instant = persistentAuditEvent.getAuditEventDate().atZone(ZoneId.systemDefault()).toInstant(); + return new AuditEvent(Date.from(instant), persistentAuditEvent.getPrincipal(), + persistentAuditEvent.getAuditEventType(), convertDataToObjects(persistentAuditEvent.getData())); + } + + /** + * Internal conversion. This is needed to support the current SpringBoot actuator AuditEventRepository interface + * + * @param data the data to convert + * @return a map of String, Object + */ + public Map convertDataToObjects(Map data) { + Map results = new HashMap<>(); + + if (data != null) { + for (Map.Entry entry : data.entrySet()) { + results.put(entry.getKey(), entry.getValue()); + } + } + return results; + } + + /** + * Internal conversion. This method will allow to save additional data. + * By default, it will save the object as string + * + * @param data the data to convert + * @return a map of String, String + */ + public Map convertDataToStrings(Map data) { + Map results = new HashMap<>(); + + if (data != null) { + for (Map.Entry entry : data.entrySet()) { + Object object = entry.getValue(); + + // Extract the data that will be saved. + if (object instanceof WebAuthenticationDetails) { + WebAuthenticationDetails authenticationDetails = (WebAuthenticationDetails) object; + results.put("remoteAddress", authenticationDetails.getRemoteAddress()); + results.put("sessionId", authenticationDetails.getSessionId()); + } else if (object != null) { + results.put(entry.getKey(), object.toString()); + } else { + results.put(entry.getKey(), "null"); + } + } + } + + return results; + } +} diff --git a/jhipster/src/main/java/com/baeldung/config/audit/package-info.java b/jhipster/src/main/java/com/baeldung/config/audit/package-info.java new file mode 100644 index 000000000000..8ed4f851f041 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/audit/package-info.java @@ -0,0 +1,4 @@ +/** + * Audit specific code. + */ +package com.baeldung.config.audit; diff --git a/jhipster/src/main/java/com/baeldung/config/package-info.java b/jhipster/src/main/java/com/baeldung/config/package-info.java new file mode 100644 index 000000000000..5e54ad6d033b --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/package-info.java @@ -0,0 +1,4 @@ +/** + * Spring Framework configuration files. + */ +package com.baeldung.config; diff --git a/jhipster/src/main/java/com/baeldung/domain/AbstractAuditingEntity.java b/jhipster/src/main/java/com/baeldung/domain/AbstractAuditingEntity.java new file mode 100644 index 000000000000..2d181e5dc84a --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/domain/AbstractAuditingEntity.java @@ -0,0 +1,80 @@ +package com.baeldung.domain; + +import java.io.Serializable; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.hibernate.envers.Audited; +import org.springframework.data.annotation.CreatedBy; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedBy; +import org.springframework.data.annotation.LastModifiedDate; + +import org.springframework.data.jpa.domain.support.AuditingEntityListener; +import java.time.ZonedDateTime; +import javax.persistence.Column; +import javax.persistence.EntityListeners; +import javax.persistence.MappedSuperclass; + +/** + * Base abstract class for entities which will hold definitions for created, last modified by and created, + * last modified by date. + */ +@MappedSuperclass +@Audited +@EntityListeners(AuditingEntityListener.class) +public abstract class AbstractAuditingEntity implements Serializable { + + private static final long serialVersionUID = 1L; + + @CreatedBy + @Column(name = "created_by", nullable = false, length = 50, updatable = false) + @JsonIgnore + private String createdBy; + + @CreatedDate + @Column(name = "created_date", nullable = false) + @JsonIgnore + private ZonedDateTime createdDate = ZonedDateTime.now(); + + @LastModifiedBy + @Column(name = "last_modified_by", length = 50) + @JsonIgnore + private String lastModifiedBy; + + @LastModifiedDate + @Column(name = "last_modified_date") + @JsonIgnore + private ZonedDateTime lastModifiedDate = ZonedDateTime.now(); + + public String getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } + + public ZonedDateTime getCreatedDate() { + return createdDate; + } + + public void setCreatedDate(ZonedDateTime createdDate) { + this.createdDate = createdDate; + } + + public String getLastModifiedBy() { + return lastModifiedBy; + } + + public void setLastModifiedBy(String lastModifiedBy) { + this.lastModifiedBy = lastModifiedBy; + } + + public ZonedDateTime getLastModifiedDate() { + return lastModifiedDate; + } + + public void setLastModifiedDate(ZonedDateTime lastModifiedDate) { + this.lastModifiedDate = lastModifiedDate; + } +} diff --git a/jhipster/src/main/java/com/baeldung/domain/Authority.java b/jhipster/src/main/java/com/baeldung/domain/Authority.java new file mode 100644 index 000000000000..8a94ad0bb250 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/domain/Authority.java @@ -0,0 +1,66 @@ +package com.baeldung.domain; + +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.Column; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.io.Serializable; + +/** + * An authority (a security role) used by Spring Security. + */ +@Entity +@Table(name = "jhi_authority") +@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) +public class Authority implements Serializable { + + private static final long serialVersionUID = 1L; + + @NotNull + @Size(min = 0, max = 50) + @Id + @Column(length = 50) + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Authority authority = (Authority) o; + + if (name != null ? !name.equals(authority.name) : authority.name != null) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + return name != null ? name.hashCode() : 0; + } + + @Override + public String toString() { + return "Authority{" + + "name='" + name + '\'' + + "}"; + } +} diff --git a/jhipster/src/main/java/com/baeldung/domain/Comment.java b/jhipster/src/main/java/com/baeldung/domain/Comment.java new file mode 100644 index 000000000000..c4587b62316f --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/domain/Comment.java @@ -0,0 +1,114 @@ +package com.baeldung.domain; + +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; + +import javax.persistence.*; +import javax.validation.constraints.*; +import java.io.Serializable; +import java.time.LocalDate; +import java.util.Objects; + +/** + * A Comment. + */ +@Entity +@Table(name = "comment") +@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) +public class Comment implements Serializable { + + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotNull + @Size(min = 10, max = 100) + @Column(name = "text", length = 100, nullable = false) + private String text; + + @NotNull + @Column(name = "creation_date", nullable = false) + private LocalDate creationDate; + + @ManyToOne(optional = false) + @NotNull + private Post post; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getText() { + return text; + } + + public Comment text(String text) { + this.text = text; + return this; + } + + public void setText(String text) { + this.text = text; + } + + public LocalDate getCreationDate() { + return creationDate; + } + + public Comment creationDate(LocalDate creationDate) { + this.creationDate = creationDate; + return this; + } + + public void setCreationDate(LocalDate creationDate) { + this.creationDate = creationDate; + } + + public Post getPost() { + return post; + } + + public Comment post(Post post) { + this.post = post; + return this; + } + + public void setPost(Post post) { + this.post = post; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Comment comment = (Comment) o; + if (comment.id == null || id == null) { + return false; + } + return Objects.equals(id, comment.id); + } + + @Override + public int hashCode() { + return Objects.hashCode(id); + } + + @Override + public String toString() { + return "Comment{" + + "id=" + id + + ", text='" + text + "'" + + ", creationDate='" + creationDate + "'" + + '}'; + } +} diff --git a/jhipster/src/main/java/com/baeldung/domain/PersistentAuditEvent.java b/jhipster/src/main/java/com/baeldung/domain/PersistentAuditEvent.java new file mode 100644 index 000000000000..c0fb0cb146c1 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/domain/PersistentAuditEvent.java @@ -0,0 +1,78 @@ +package com.baeldung.domain; + + +import java.io.Serializable; +import java.time.LocalDateTime; +import javax.persistence.*; +import javax.validation.constraints.NotNull; +import java.util.HashMap; +import java.util.Map; + +/** + * Persist AuditEvent managed by the Spring Boot actuator + * @see org.springframework.boot.actuate.audit.AuditEvent + */ +@Entity +@Table(name = "jhi_persistent_audit_event") +public class PersistentAuditEvent implements Serializable { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "event_id") + private Long id; + + @NotNull + @Column(nullable = false) + private String principal; + + @Column(name = "event_date") + private LocalDateTime auditEventDate; + @Column(name = "event_type") + private String auditEventType; + + @ElementCollection + @MapKeyColumn(name = "name") + @Column(name = "value") + @CollectionTable(name = "jhi_persistent_audit_evt_data", joinColumns=@JoinColumn(name="event_id")) + private Map data = new HashMap<>(); + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getPrincipal() { + return principal; + } + + public void setPrincipal(String principal) { + this.principal = principal; + } + + public LocalDateTime getAuditEventDate() { + return auditEventDate; + } + + public void setAuditEventDate(LocalDateTime auditEventDate) { + this.auditEventDate = auditEventDate; + } + + public String getAuditEventType() { + return auditEventType; + } + + public void setAuditEventType(String auditEventType) { + this.auditEventType = auditEventType; + } + + public Map getData() { + return data; + } + + public void setData(Map data) { + this.data = data; + } +} diff --git a/jhipster/src/main/java/com/baeldung/domain/Post.java b/jhipster/src/main/java/com/baeldung/domain/Post.java new file mode 100644 index 000000000000..7f084dc1770a --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/domain/Post.java @@ -0,0 +1,133 @@ +package com.baeldung.domain; + +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; + +import javax.persistence.*; +import javax.validation.constraints.*; +import java.io.Serializable; +import java.time.LocalDate; +import java.util.Objects; + +/** + * A Post. + */ +@Entity +@Table(name = "post") +@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) +public class Post implements Serializable { + + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotNull + @Size(min = 10, max = 100) + @Column(name = "title", length = 100, nullable = false) + private String title; + + @NotNull + @Size(min = 10, max = 1000) + @Column(name = "content", length = 1000, nullable = false) + private String content; + + @NotNull + @Column(name = "creation_date", nullable = false) + private LocalDate creationDate; + + @ManyToOne(optional = false) + @NotNull + private User creator; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public Post title(String title) { + this.title = title; + return this; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getContent() { + return content; + } + + public Post content(String content) { + this.content = content; + return this; + } + + public void setContent(String content) { + this.content = content; + } + + public LocalDate getCreationDate() { + return creationDate; + } + + public Post creationDate(LocalDate creationDate) { + this.creationDate = creationDate; + return this; + } + + public void setCreationDate(LocalDate creationDate) { + this.creationDate = creationDate; + } + + public User getCreator() { + return creator; + } + + public Post creator(User user) { + this.creator = user; + return this; + } + + public void setCreator(User user) { + this.creator = user; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Post post = (Post) o; + if (post.id == null || id == null) { + return false; + } + return Objects.equals(id, post.id); + } + + @Override + public int hashCode() { + return Objects.hashCode(id); + } + + @Override + public String toString() { + return "Post{" + + "id=" + id + + ", title='" + title + "'" + + ", content='" + content + "'" + + ", creationDate='" + creationDate + "'" + + '}'; + } +} diff --git a/jhipster/src/main/java/com/baeldung/domain/User.java b/jhipster/src/main/java/com/baeldung/domain/User.java new file mode 100644 index 000000000000..76aa3a520994 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/domain/User.java @@ -0,0 +1,235 @@ +package com.baeldung.domain; + +import com.baeldung.config.Constants; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.hibernate.annotations.BatchSize; +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.hibernate.validator.constraints.Email; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; +import java.io.Serializable; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; +import java.time.ZonedDateTime; + +/** + * A user. + */ +@Entity +@Table(name = "jhi_user") +@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) +public class User extends AbstractAuditingEntity implements Serializable { + + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotNull + @Pattern(regexp = Constants.LOGIN_REGEX) + @Size(min = 1, max = 50) + @Column(length = 50, unique = true, nullable = false) + private String login; + + @JsonIgnore + @NotNull + @Size(min = 60, max = 60) + @Column(name = "password_hash",length = 60) + private String password; + + @Size(max = 50) + @Column(name = "first_name", length = 50) + private String firstName; + + @Size(max = 50) + @Column(name = "last_name", length = 50) + private String lastName; + + @Email + @Size(max = 100) + @Column(length = 100, unique = true) + private String email; + + @NotNull + @Column(nullable = false) + private boolean activated = false; + + @Size(min = 2, max = 5) + @Column(name = "lang_key", length = 5) + private String langKey; + + @Size(max = 256) + @Column(name = "image_url", length = 256) + private String imageUrl; + + @Size(max = 20) + @Column(name = "activation_key", length = 20) + @JsonIgnore + private String activationKey; + + @Size(max = 20) + @Column(name = "reset_key", length = 20) + private String resetKey; + + @Column(name = "reset_date") + private ZonedDateTime resetDate = null; + + @JsonIgnore + @ManyToMany + @JoinTable( + name = "jhi_user_authority", + joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")}, + inverseJoinColumns = {@JoinColumn(name = "authority_name", referencedColumnName = "name")}) + @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) + @BatchSize(size = 20) + private Set authorities = new HashSet<>(); + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getLogin() { + return login; + } + + //Lowercase the login before saving it in database + public void setLogin(String login) { + this.login = login.toLowerCase(Locale.ENGLISH); + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getImageUrl() { + return imageUrl; + } + + public void setImageUrl(String imageUrl) { + this.imageUrl = imageUrl; + } + + public boolean getActivated() { + return activated; + } + + public void setActivated(boolean activated) { + this.activated = activated; + } + + public String getActivationKey() { + return activationKey; + } + + public void setActivationKey(String activationKey) { + this.activationKey = activationKey; + } + + public String getResetKey() { + return resetKey; + } + + public void setResetKey(String resetKey) { + this.resetKey = resetKey; + } + + public ZonedDateTime getResetDate() { + return resetDate; + } + + public void setResetDate(ZonedDateTime resetDate) { + this.resetDate = resetDate; + } + + public String getLangKey() { + return langKey; + } + + public void setLangKey(String langKey) { + this.langKey = langKey; + } + + public Set getAuthorities() { + return authorities; + } + + public void setAuthorities(Set authorities) { + this.authorities = authorities; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + User user = (User) o; + + if (!login.equals(user.login)) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + return login.hashCode(); + } + + @Override + public String toString() { + return "User{" + + "login='" + login + '\'' + + ", firstName='" + firstName + '\'' + + ", lastName='" + lastName + '\'' + + ", email='" + email + '\'' + + ", imageUrl='" + imageUrl + '\'' + + ", activated='" + activated + '\'' + + ", langKey='" + langKey + '\'' + + ", activationKey='" + activationKey + '\'' + + "}"; + } +} diff --git a/jhipster/src/main/java/com/baeldung/domain/package-info.java b/jhipster/src/main/java/com/baeldung/domain/package-info.java new file mode 100644 index 000000000000..2492048c77e6 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/domain/package-info.java @@ -0,0 +1,4 @@ +/** + * JPA domain objects. + */ +package com.baeldung.domain; diff --git a/jhipster/src/main/java/com/baeldung/repository/AuthorityRepository.java b/jhipster/src/main/java/com/baeldung/repository/AuthorityRepository.java new file mode 100644 index 000000000000..fab63174aaa2 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/repository/AuthorityRepository.java @@ -0,0 +1,11 @@ +package com.baeldung.repository; + +import com.baeldung.domain.Authority; + +import org.springframework.data.jpa.repository.JpaRepository; + +/** + * Spring Data JPA repository for the Authority entity. + */ +public interface AuthorityRepository extends JpaRepository { +} diff --git a/jhipster/src/main/java/com/baeldung/repository/CommentRepository.java b/jhipster/src/main/java/com/baeldung/repository/CommentRepository.java new file mode 100644 index 000000000000..d2d2618c36ec --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/repository/CommentRepository.java @@ -0,0 +1,15 @@ +package com.baeldung.repository; + +import com.baeldung.domain.Comment; + +import org.springframework.data.jpa.repository.*; + +import java.util.List; + +/** + * Spring Data JPA repository for the Comment entity. + */ +@SuppressWarnings("unused") +public interface CommentRepository extends JpaRepository { + +} diff --git a/jhipster/src/main/java/com/baeldung/repository/CustomAuditEventRepository.java b/jhipster/src/main/java/com/baeldung/repository/CustomAuditEventRepository.java new file mode 100644 index 000000000000..fcfa9baeec98 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/repository/CustomAuditEventRepository.java @@ -0,0 +1,81 @@ +package com.baeldung.repository; + +import com.baeldung.config.Constants; +import com.baeldung.config.audit.AuditEventConverter; +import com.baeldung.domain.PersistentAuditEvent; + +import org.springframework.boot.actuate.audit.AuditEvent; +import org.springframework.boot.actuate.audit.AuditEventRepository; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Date; +import java.util.List; + +/** + * An implementation of Spring Boot's AuditEventRepository. + */ +@Repository +public class CustomAuditEventRepository implements AuditEventRepository { + + private static final String AUTHORIZATION_FAILURE = "AUTHORIZATION_FAILURE"; + + private final PersistenceAuditEventRepository persistenceAuditEventRepository; + + private final AuditEventConverter auditEventConverter; + + public CustomAuditEventRepository(PersistenceAuditEventRepository persistenceAuditEventRepository, + AuditEventConverter auditEventConverter) { + + this.persistenceAuditEventRepository = persistenceAuditEventRepository; + this.auditEventConverter = auditEventConverter; + } + + @Override + public List find(Date after) { + Iterable persistentAuditEvents = + persistenceAuditEventRepository.findByAuditEventDateAfter(LocalDateTime.from(after.toInstant())); + return auditEventConverter.convertToAuditEvent(persistentAuditEvents); + } + + @Override + public List find(String principal, Date after) { + Iterable persistentAuditEvents; + if (principal == null && after == null) { + persistentAuditEvents = persistenceAuditEventRepository.findAll(); + } else if (after == null) { + persistentAuditEvents = persistenceAuditEventRepository.findByPrincipal(principal); + } else { + persistentAuditEvents = + persistenceAuditEventRepository.findByPrincipalAndAuditEventDateAfter(principal, LocalDateTime.from(after.toInstant())); + } + return auditEventConverter.convertToAuditEvent(persistentAuditEvents); + } + + @Override + public List find(String principal, Date after, String type) { + Iterable persistentAuditEvents = + persistenceAuditEventRepository.findByPrincipalAndAuditEventDateAfterAndAuditEventType(principal, LocalDateTime.from(after.toInstant()), type); + return auditEventConverter.convertToAuditEvent(persistentAuditEvents); + } + + @Override + @Transactional(propagation = Propagation.REQUIRES_NEW) + public void add(AuditEvent event) { + if (!AUTHORIZATION_FAILURE.equals(event.getType()) && + !Constants.ANONYMOUS_USER.equals(event.getPrincipal())) { + + PersistentAuditEvent persistentAuditEvent = new PersistentAuditEvent(); + persistentAuditEvent.setPrincipal(event.getPrincipal()); + persistentAuditEvent.setAuditEventType(event.getType()); + Instant instant = Instant.ofEpochMilli(event.getTimestamp().getTime()); + persistentAuditEvent.setAuditEventDate(LocalDateTime.ofInstant(instant, ZoneId.systemDefault())); + persistentAuditEvent.setData(auditEventConverter.convertDataToStrings(event.getData())); + persistenceAuditEventRepository.save(persistentAuditEvent); + } + } +} diff --git a/jhipster/src/main/java/com/baeldung/repository/PersistenceAuditEventRepository.java b/jhipster/src/main/java/com/baeldung/repository/PersistenceAuditEventRepository.java new file mode 100644 index 000000000000..f4b7f1c5bf32 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/repository/PersistenceAuditEventRepository.java @@ -0,0 +1,26 @@ +package com.baeldung.repository; + +import com.baeldung.domain.PersistentAuditEvent; + +import java.time.LocalDateTime; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +import java.util.List; + +/** + * Spring Data JPA repository for the PersistentAuditEvent entity. + */ +public interface PersistenceAuditEventRepository extends JpaRepository { + + List findByPrincipal(String principal); + + List findByAuditEventDateAfter(LocalDateTime after); + + List findByPrincipalAndAuditEventDateAfter(String principal, LocalDateTime after); + + List findByPrincipalAndAuditEventDateAfterAndAuditEventType(String principle, LocalDateTime after, String type); + + Page findAllByAuditEventDateBetween(LocalDateTime fromDate, LocalDateTime toDate, Pageable pageable); +} diff --git a/jhipster/src/main/java/com/baeldung/repository/PostRepository.java b/jhipster/src/main/java/com/baeldung/repository/PostRepository.java new file mode 100644 index 000000000000..22e8aa1372a8 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/repository/PostRepository.java @@ -0,0 +1,18 @@ +package com.baeldung.repository; + +import com.baeldung.domain.Post; + +import org.springframework.data.jpa.repository.*; + +import java.util.List; + +/** + * Spring Data JPA repository for the Post entity. + */ +@SuppressWarnings("unused") +public interface PostRepository extends JpaRepository { + + @Query("select post from Post post where post.creator.login = ?#{principal.username}") + List findByCreatorIsCurrentUser(); + +} diff --git a/jhipster/src/main/java/com/baeldung/repository/UserRepository.java b/jhipster/src/main/java/com/baeldung/repository/UserRepository.java new file mode 100644 index 000000000000..a8f81499107b --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/repository/UserRepository.java @@ -0,0 +1,37 @@ +package com.baeldung.repository; + +import com.baeldung.domain.User; + +import java.time.ZonedDateTime; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; +import java.util.Optional; + +/** + * Spring Data JPA repository for the User entity. + */ +public interface UserRepository extends JpaRepository { + + Optional findOneByActivationKey(String activationKey); + + List findAllByActivatedIsFalseAndCreatedDateBefore(ZonedDateTime dateTime); + + Optional findOneByResetKey(String resetKey); + + Optional findOneByEmail(String email); + + Optional findOneByLogin(String login); + + @EntityGraph(attributePaths = "authorities") + User findOneWithAuthoritiesById(Long id); + + @EntityGraph(attributePaths = "authorities") + Optional findOneWithAuthoritiesByLogin(String login); + + Page findAllByLoginNot(Pageable pageable, String login); +} diff --git a/jhipster/src/main/java/com/baeldung/repository/package-info.java b/jhipster/src/main/java/com/baeldung/repository/package-info.java new file mode 100644 index 000000000000..9d1fb837c32d --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/repository/package-info.java @@ -0,0 +1,4 @@ +/** + * Spring Data JPA repositories. + */ +package com.baeldung.repository; diff --git a/jhipster/src/main/java/com/baeldung/security/AuthoritiesConstants.java b/jhipster/src/main/java/com/baeldung/security/AuthoritiesConstants.java new file mode 100644 index 000000000000..40cf54e4f610 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/security/AuthoritiesConstants.java @@ -0,0 +1,16 @@ +package com.baeldung.security; + +/** + * Constants for Spring Security authorities. + */ +public final class AuthoritiesConstants { + + public static final String ADMIN = "ROLE_ADMIN"; + + public static final String USER = "ROLE_USER"; + + public static final String ANONYMOUS = "ROLE_ANONYMOUS"; + + private AuthoritiesConstants() { + } +} diff --git a/jhipster/src/main/java/com/baeldung/security/DomainUserDetailsService.java b/jhipster/src/main/java/com/baeldung/security/DomainUserDetailsService.java new file mode 100644 index 000000000000..6ce07739f719 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/security/DomainUserDetailsService.java @@ -0,0 +1,51 @@ +package com.baeldung.security; + +import com.baeldung.domain.User; +import com.baeldung.repository.UserRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * Authenticate a user from the database. + */ +@Component("userDetailsService") +public class DomainUserDetailsService implements UserDetailsService { + + private final Logger log = LoggerFactory.getLogger(DomainUserDetailsService.class); + + private final UserRepository userRepository; + + public DomainUserDetailsService(UserRepository userRepository) { + this.userRepository = userRepository; + } + + @Override + @Transactional + public UserDetails loadUserByUsername(final String login) { + log.debug("Authenticating {}", login); + String lowercaseLogin = login.toLowerCase(Locale.ENGLISH); + Optional userFromDatabase = userRepository.findOneWithAuthoritiesByLogin(lowercaseLogin); + return userFromDatabase.map(user -> { + if (!user.getActivated()) { + throw new UserNotActivatedException("User " + lowercaseLogin + " was not activated"); + } + List grantedAuthorities = user.getAuthorities().stream() + .map(authority -> new SimpleGrantedAuthority(authority.getName())) + .collect(Collectors.toList()); + return new org.springframework.security.core.userdetails.User(lowercaseLogin, + user.getPassword(), + grantedAuthorities); + }).orElseThrow(() -> new UsernameNotFoundException("User " + lowercaseLogin + " was not found in the " + + "database")); + } +} diff --git a/jhipster/src/main/java/com/baeldung/security/SecurityUtils.java b/jhipster/src/main/java/com/baeldung/security/SecurityUtils.java new file mode 100644 index 000000000000..96aaef380564 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/security/SecurityUtils.java @@ -0,0 +1,68 @@ +package com.baeldung.security; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; + +/** + * Utility class for Spring Security. + */ +public final class SecurityUtils { + + private SecurityUtils() { + } + + /** + * Get the login of the current user. + * + * @return the login of the current user + */ + public static String getCurrentUserLogin() { + SecurityContext securityContext = SecurityContextHolder.getContext(); + Authentication authentication = securityContext.getAuthentication(); + String userName = null; + if (authentication != null) { + if (authentication.getPrincipal() instanceof UserDetails) { + UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal(); + userName = springSecurityUser.getUsername(); + } else if (authentication.getPrincipal() instanceof String) { + userName = (String) authentication.getPrincipal(); + } + } + return userName; + } + + /** + * Check if a user is authenticated. + * + * @return true if the user is authenticated, false otherwise + */ + public static boolean isAuthenticated() { + SecurityContext securityContext = SecurityContextHolder.getContext(); + Authentication authentication = securityContext.getAuthentication(); + if (authentication != null) { + return authentication.getAuthorities().stream() + .noneMatch(grantedAuthority -> grantedAuthority.getAuthority().equals(AuthoritiesConstants.ANONYMOUS)); + } + return false; + } + + /** + * If the current user has a specific authority (security role). + * + *

The name of this method comes from the isUserInRole() method in the Servlet API

+ * + * @param authority the authority to check + * @return true if the current user has the authority, false otherwise + */ + public static boolean isCurrentUserInRole(String authority) { + SecurityContext securityContext = SecurityContextHolder.getContext(); + Authentication authentication = securityContext.getAuthentication(); + if (authentication != null) { + return authentication.getAuthorities().stream() + .anyMatch(grantedAuthority -> grantedAuthority.getAuthority().equals(authority)); + } + return false; + } +} diff --git a/jhipster/src/main/java/com/baeldung/security/SpringSecurityAuditorAware.java b/jhipster/src/main/java/com/baeldung/security/SpringSecurityAuditorAware.java new file mode 100644 index 000000000000..d59917153cf1 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/security/SpringSecurityAuditorAware.java @@ -0,0 +1,19 @@ +package com.baeldung.security; + +import com.baeldung.config.Constants; + +import org.springframework.data.domain.AuditorAware; +import org.springframework.stereotype.Component; + +/** + * Implementation of AuditorAware based on Spring Security. + */ +@Component +public class SpringSecurityAuditorAware implements AuditorAware { + + @Override + public String getCurrentAuditor() { + String userName = SecurityUtils.getCurrentUserLogin(); + return userName != null ? userName : Constants.SYSTEM_ACCOUNT; + } +} diff --git a/jhipster/src/main/java/com/baeldung/security/UserNotActivatedException.java b/jhipster/src/main/java/com/baeldung/security/UserNotActivatedException.java new file mode 100644 index 000000000000..7265188cad6b --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/security/UserNotActivatedException.java @@ -0,0 +1,19 @@ +package com.baeldung.security; + +import org.springframework.security.core.AuthenticationException; + +/** + * This exception is thrown in case of a not activated user trying to authenticate. + */ +public class UserNotActivatedException extends AuthenticationException { + + private static final long serialVersionUID = 1L; + + public UserNotActivatedException(String message) { + super(message); + } + + public UserNotActivatedException(String message, Throwable t) { + super(message, t); + } +} diff --git a/jhipster/src/main/java/com/baeldung/security/jwt/JWTConfigurer.java b/jhipster/src/main/java/com/baeldung/security/jwt/JWTConfigurer.java new file mode 100644 index 000000000000..5720812b9f25 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/security/jwt/JWTConfigurer.java @@ -0,0 +1,23 @@ +package com.baeldung.security.jwt; + +import org.springframework.security.config.annotation.SecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.web.DefaultSecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +public class JWTConfigurer extends SecurityConfigurerAdapter { + + public static final String AUTHORIZATION_HEADER = "Authorization"; + + private TokenProvider tokenProvider; + + public JWTConfigurer(TokenProvider tokenProvider) { + this.tokenProvider = tokenProvider; + } + + @Override + public void configure(HttpSecurity http) throws Exception { + JWTFilter customFilter = new JWTFilter(tokenProvider); + http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class); + } +} diff --git a/jhipster/src/main/java/com/baeldung/security/jwt/JWTFilter.java b/jhipster/src/main/java/com/baeldung/security/jwt/JWTFilter.java new file mode 100644 index 000000000000..89b1947e058d --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/security/jwt/JWTFilter.java @@ -0,0 +1,58 @@ +package com.baeldung.security.jwt; + +import java.io.IOException; +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.util.StringUtils; +import org.springframework.web.filter.GenericFilterBean; + +import io.jsonwebtoken.ExpiredJwtException; + +/** + * Filters incoming requests and installs a Spring Security principal if a header corresponding to a valid user is + * found. + */ +public class JWTFilter extends GenericFilterBean { + + private final Logger log = LoggerFactory.getLogger(JWTFilter.class); + + private TokenProvider tokenProvider; + + public JWTFilter(TokenProvider tokenProvider) { + this.tokenProvider = tokenProvider; + } + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) + throws IOException, ServletException { + try { + HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; + String jwt = resolveToken(httpServletRequest); + if (StringUtils.hasText(jwt) && this.tokenProvider.validateToken(jwt)) { + Authentication authentication = this.tokenProvider.getAuthentication(jwt); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + filterChain.doFilter(servletRequest, servletResponse); + } catch (ExpiredJwtException eje) { + log.info("Security exception for user {} - {}", + eje.getClaims().getSubject(), eje.getMessage()); + + log.trace("Security exception trace: {}", eje); + ((HttpServletResponse) servletResponse).setStatus(HttpServletResponse.SC_UNAUTHORIZED); + } + } + + private String resolveToken(HttpServletRequest request){ + String bearerToken = request.getHeader(JWTConfigurer.AUTHORIZATION_HEADER); + if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { + return bearerToken.substring(7, bearerToken.length()); + } + return null; + } +} diff --git a/jhipster/src/main/java/com/baeldung/security/jwt/TokenProvider.java b/jhipster/src/main/java/com/baeldung/security/jwt/TokenProvider.java new file mode 100644 index 000000000000..3ba4d7c793d2 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/security/jwt/TokenProvider.java @@ -0,0 +1,109 @@ +package com.baeldung.security.jwt; + +import io.github.jhipster.config.JHipsterProperties; + +import java.util.*; +import java.util.stream.Collectors; +import javax.annotation.PostConstruct; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.User; +import org.springframework.stereotype.Component; + +import io.jsonwebtoken.*; + +@Component +public class TokenProvider { + + private final Logger log = LoggerFactory.getLogger(TokenProvider.class); + + private static final String AUTHORITIES_KEY = "auth"; + + private String secretKey; + + private long tokenValidityInMilliseconds; + + private long tokenValidityInMillisecondsForRememberMe; + + private final JHipsterProperties jHipsterProperties; + + public TokenProvider(JHipsterProperties jHipsterProperties) { + this.jHipsterProperties = jHipsterProperties; + } + + @PostConstruct + public void init() { + this.secretKey = + jHipsterProperties.getSecurity().getAuthentication().getJwt().getSecret(); + + this.tokenValidityInMilliseconds = + 1000 * jHipsterProperties.getSecurity().getAuthentication().getJwt().getTokenValidityInSeconds(); + this.tokenValidityInMillisecondsForRememberMe = + 1000 * jHipsterProperties.getSecurity().getAuthentication().getJwt().getTokenValidityInSecondsForRememberMe(); + } + + public String createToken(Authentication authentication, Boolean rememberMe) { + String authorities = authentication.getAuthorities().stream() + .map(GrantedAuthority::getAuthority) + .collect(Collectors.joining(",")); + + long now = (new Date()).getTime(); + Date validity; + if (rememberMe) { + validity = new Date(now + this.tokenValidityInMillisecondsForRememberMe); + } else { + validity = new Date(now + this.tokenValidityInMilliseconds); + } + + return Jwts.builder() + .setSubject(authentication.getName()) + .claim(AUTHORITIES_KEY, authorities) + .signWith(SignatureAlgorithm.HS512, secretKey) + .setExpiration(validity) + .compact(); + } + + public Authentication getAuthentication(String token) { + Claims claims = Jwts.parser() + .setSigningKey(secretKey) + .parseClaimsJws(token) + .getBody(); + + Collection authorities = + Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(",")) + .map(SimpleGrantedAuthority::new) + .collect(Collectors.toList()); + + User principal = new User(claims.getSubject(), "", authorities); + + return new UsernamePasswordAuthenticationToken(principal, "", authorities); + } + + public boolean validateToken(String authToken) { + try { + Jwts.parser().setSigningKey(secretKey).parseClaimsJws(authToken); + return true; + } catch (SignatureException e) { + log.info("Invalid JWT signature."); + log.trace("Invalid JWT signature trace: {}", e); + } catch (MalformedJwtException e) { + log.info("Invalid JWT token."); + log.trace("Invalid JWT token trace: {}", e); + } catch (ExpiredJwtException e) { + log.info("Expired JWT token."); + log.trace("Expired JWT token trace: {}", e); + } catch (UnsupportedJwtException e) { + log.info("Unsupported JWT token."); + log.trace("Unsupported JWT token trace: {}", e); + } catch (IllegalArgumentException e) { + log.info("JWT token compact of handler are invalid."); + log.trace("JWT token compact of handler are invalid trace: {}", e); + } + return false; + } +} diff --git a/jhipster/src/main/java/com/baeldung/security/package-info.java b/jhipster/src/main/java/com/baeldung/security/package-info.java new file mode 100644 index 000000000000..1e29c15b4e2f --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/security/package-info.java @@ -0,0 +1,4 @@ +/** + * Spring Security configuration. + */ +package com.baeldung.security; diff --git a/jhipster/src/main/java/com/baeldung/service/AuditEventService.java b/jhipster/src/main/java/com/baeldung/service/AuditEventService.java new file mode 100644 index 000000000000..87018549222a --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/service/AuditEventService.java @@ -0,0 +1,50 @@ +package com.baeldung.service; + +import com.baeldung.config.audit.AuditEventConverter; +import com.baeldung.repository.PersistenceAuditEventRepository; +import java.time.LocalDateTime; +import org.springframework.boot.actuate.audit.AuditEvent; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Optional; + +/** + * Service for managing audit events. + *

+ * This is the default implementation to support SpringBoot Actuator AuditEventRepository + *

+ */ +@Service +@Transactional +public class AuditEventService { + + private final PersistenceAuditEventRepository persistenceAuditEventRepository; + + private final AuditEventConverter auditEventConverter; + + public AuditEventService( + PersistenceAuditEventRepository persistenceAuditEventRepository, + AuditEventConverter auditEventConverter) { + + this.persistenceAuditEventRepository = persistenceAuditEventRepository; + this.auditEventConverter = auditEventConverter; + } + + public Page findAll(Pageable pageable) { + return persistenceAuditEventRepository.findAll(pageable) + .map(auditEventConverter::convertToAuditEvent); + } + + public Page findByDates(LocalDateTime fromDate, LocalDateTime toDate, Pageable pageable) { + return persistenceAuditEventRepository.findAllByAuditEventDateBetween(fromDate, toDate, pageable) + .map(auditEventConverter::convertToAuditEvent); + } + + public Optional find(Long id) { + return Optional.ofNullable(persistenceAuditEventRepository.findOne(id)).map + (auditEventConverter::convertToAuditEvent); + } +} diff --git a/jhipster/src/main/java/com/baeldung/service/MailService.java b/jhipster/src/main/java/com/baeldung/service/MailService.java new file mode 100644 index 000000000000..9f85531da733 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/service/MailService.java @@ -0,0 +1,108 @@ +package com.baeldung.service; + +import com.baeldung.domain.User; + +import io.github.jhipster.config.JHipsterProperties; + +import org.apache.commons.lang3.CharEncoding; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.MessageSource; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.MimeMessageHelper; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.thymeleaf.context.Context; +import org.thymeleaf.spring4.SpringTemplateEngine; + +import javax.mail.internet.MimeMessage; +import java.util.Locale; + +/** + * Service for sending e-mails. + *

+ * We use the @Async annotation to send e-mails asynchronously. + *

+ */ +@Service +public class MailService { + + private final Logger log = LoggerFactory.getLogger(MailService.class); + + private static final String USER = "user"; + + private static final String BASE_URL = "baseUrl"; + + private final JHipsterProperties jHipsterProperties; + + private final JavaMailSender javaMailSender; + + private final MessageSource messageSource; + + private final SpringTemplateEngine templateEngine; + + public MailService(JHipsterProperties jHipsterProperties, JavaMailSender javaMailSender, + MessageSource messageSource, SpringTemplateEngine templateEngine) { + + this.jHipsterProperties = jHipsterProperties; + this.javaMailSender = javaMailSender; + this.messageSource = messageSource; + this.templateEngine = templateEngine; + } + + @Async + public void sendEmail(String to, String subject, String content, boolean isMultipart, boolean isHtml) { + log.debug("Send e-mail[multipart '{}' and html '{}'] to '{}' with subject '{}' and content={}", + isMultipart, isHtml, to, subject, content); + + // Prepare message using a Spring helper + MimeMessage mimeMessage = javaMailSender.createMimeMessage(); + try { + MimeMessageHelper message = new MimeMessageHelper(mimeMessage, isMultipart, CharEncoding.UTF_8); + message.setTo(to); + message.setFrom(jHipsterProperties.getMail().getFrom()); + message.setSubject(subject); + message.setText(content, isHtml); + javaMailSender.send(mimeMessage); + log.debug("Sent e-mail to User '{}'", to); + } catch (Exception e) { + log.warn("E-mail could not be sent to user '{}'", to, e); + } + } + + @Async + public void sendActivationEmail(User user) { + log.debug("Sending activation e-mail to '{}'", user.getEmail()); + Locale locale = Locale.forLanguageTag(user.getLangKey()); + Context context = new Context(locale); + context.setVariable(USER, user); + context.setVariable(BASE_URL, jHipsterProperties.getMail().getBaseUrl()); + String content = templateEngine.process("activationEmail", context); + String subject = messageSource.getMessage("email.activation.title", null, locale); + sendEmail(user.getEmail(), subject, content, false, true); + } + + @Async + public void sendCreationEmail(User user) { + log.debug("Sending creation e-mail to '{}'", user.getEmail()); + Locale locale = Locale.forLanguageTag(user.getLangKey()); + Context context = new Context(locale); + context.setVariable(USER, user); + context.setVariable(BASE_URL, jHipsterProperties.getMail().getBaseUrl()); + String content = templateEngine.process("creationEmail", context); + String subject = messageSource.getMessage("email.activation.title", null, locale); + sendEmail(user.getEmail(), subject, content, false, true); + } + + @Async + public void sendPasswordResetMail(User user) { + log.debug("Sending password reset e-mail to '{}'", user.getEmail()); + Locale locale = Locale.forLanguageTag(user.getLangKey()); + Context context = new Context(locale); + context.setVariable(USER, user); + context.setVariable(BASE_URL, jHipsterProperties.getMail().getBaseUrl()); + String content = templateEngine.process("passwordResetEmail", context); + String subject = messageSource.getMessage("email.reset.title", null, locale); + sendEmail(user.getEmail(), subject, content, false, true); + } +} diff --git a/jhipster/src/main/java/com/baeldung/service/UserService.java b/jhipster/src/main/java/com/baeldung/service/UserService.java new file mode 100644 index 000000000000..7b9096e0dade --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/service/UserService.java @@ -0,0 +1,228 @@ +package com.baeldung.service; + +import com.baeldung.domain.Authority; +import com.baeldung.domain.User; +import com.baeldung.repository.AuthorityRepository; +import com.baeldung.config.Constants; +import com.baeldung.repository.UserRepository; +import com.baeldung.security.AuthoritiesConstants; +import com.baeldung.security.SecurityUtils; +import com.baeldung.service.util.RandomUtil; +import com.baeldung.service.dto.UserDTO; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.ZonedDateTime; +import java.util.*; + +/** + * Service class for managing users. + */ +@Service +@Transactional +public class UserService { + + private final Logger log = LoggerFactory.getLogger(UserService.class); + + private final UserRepository userRepository; + + private final PasswordEncoder passwordEncoder; + + private final AuthorityRepository authorityRepository; + + public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder, AuthorityRepository authorityRepository) { + this.userRepository = userRepository; + this.passwordEncoder = passwordEncoder; + this.authorityRepository = authorityRepository; + } + + public Optional activateRegistration(String key) { + log.debug("Activating user for activation key {}", key); + return userRepository.findOneByActivationKey(key) + .map(user -> { + // activate given user for the registration key. + user.setActivated(true); + user.setActivationKey(null); + log.debug("Activated user: {}", user); + return user; + }); + } + + public Optional completePasswordReset(String newPassword, String key) { + log.debug("Reset user password for reset key {}", key); + + return userRepository.findOneByResetKey(key) + .filter(user -> { + ZonedDateTime oneDayAgo = ZonedDateTime.now().minusHours(24); + return user.getResetDate().isAfter(oneDayAgo); + }) + .map(user -> { + user.setPassword(passwordEncoder.encode(newPassword)); + user.setResetKey(null); + user.setResetDate(null); + return user; + }); + } + + public Optional requestPasswordReset(String mail) { + return userRepository.findOneByEmail(mail) + .filter(User::getActivated) + .map(user -> { + user.setResetKey(RandomUtil.generateResetKey()); + user.setResetDate(ZonedDateTime.now()); + return user; + }); + } + + public User createUser(String login, String password, String firstName, String lastName, String email, + String imageUrl, String langKey) { + + User newUser = new User(); + Authority authority = authorityRepository.findOne(AuthoritiesConstants.USER); + Set authorities = new HashSet<>(); + String encryptedPassword = passwordEncoder.encode(password); + newUser.setLogin(login); + // new user gets initially a generated password + newUser.setPassword(encryptedPassword); + newUser.setFirstName(firstName); + newUser.setLastName(lastName); + newUser.setEmail(email); + newUser.setImageUrl(imageUrl); + newUser.setLangKey(langKey); + // new user is not active + newUser.setActivated(false); + // new user gets registration key + newUser.setActivationKey(RandomUtil.generateActivationKey()); + authorities.add(authority); + newUser.setAuthorities(authorities); + userRepository.save(newUser); + log.debug("Created Information for User: {}", newUser); + return newUser; + } + + public User createUser(UserDTO userDTO) { + User user = new User(); + user.setLogin(userDTO.getLogin()); + user.setFirstName(userDTO.getFirstName()); + user.setLastName(userDTO.getLastName()); + user.setEmail(userDTO.getEmail()); + user.setImageUrl(userDTO.getImageUrl()); + if (userDTO.getLangKey() == null) { + user.setLangKey("en"); // default language + } else { + user.setLangKey(userDTO.getLangKey()); + } + if (userDTO.getAuthorities() != null) { + Set authorities = new HashSet<>(); + userDTO.getAuthorities().forEach( + authority -> authorities.add(authorityRepository.findOne(authority)) + ); + user.setAuthorities(authorities); + } + String encryptedPassword = passwordEncoder.encode(RandomUtil.generatePassword()); + user.setPassword(encryptedPassword); + user.setResetKey(RandomUtil.generateResetKey()); + user.setResetDate(ZonedDateTime.now()); + user.setActivated(true); + userRepository.save(user); + log.debug("Created Information for User: {}", user); + return user; + } + + /** + * Update basic information (first name, last name, email, language) for the current user. + */ + public void updateUser(String firstName, String lastName, String email, String langKey) { + userRepository.findOneByLogin(SecurityUtils.getCurrentUserLogin()).ifPresent(user -> { + user.setFirstName(firstName); + user.setLastName(lastName); + user.setEmail(email); + user.setLangKey(langKey); + log.debug("Changed Information for User: {}", user); + }); + } + + /** + * Update all information for a specific user, and return the modified user. + */ + public Optional updateUser(UserDTO userDTO) { + return Optional.of(userRepository + .findOne(userDTO.getId())) + .map(user -> { + user.setLogin(userDTO.getLogin()); + user.setFirstName(userDTO.getFirstName()); + user.setLastName(userDTO.getLastName()); + user.setEmail(userDTO.getEmail()); + user.setImageUrl(userDTO.getImageUrl()); + user.setActivated(userDTO.isActivated()); + user.setLangKey(userDTO.getLangKey()); + Set managedAuthorities = user.getAuthorities(); + managedAuthorities.clear(); + userDTO.getAuthorities().stream() + .map(authorityRepository::findOne) + .forEach(managedAuthorities::add); + log.debug("Changed Information for User: {}", user); + return user; + }) + .map(UserDTO::new); + } + + public void deleteUser(String login) { + userRepository.findOneByLogin(login).ifPresent(user -> { + userRepository.delete(user); + log.debug("Deleted User: {}", user); + }); + } + + public void changePassword(String password) { + userRepository.findOneByLogin(SecurityUtils.getCurrentUserLogin()).ifPresent(user -> { + String encryptedPassword = passwordEncoder.encode(password); + user.setPassword(encryptedPassword); + log.debug("Changed password for User: {}", user); + }); + } + + @Transactional(readOnly = true) + public Page getAllManagedUsers(Pageable pageable) { + return userRepository.findAllByLoginNot(pageable, Constants.ANONYMOUS_USER).map(UserDTO::new); + } + + @Transactional(readOnly = true) + public Optional getUserWithAuthoritiesByLogin(String login) { + return userRepository.findOneWithAuthoritiesByLogin(login); + } + + @Transactional(readOnly = true) + public User getUserWithAuthorities(Long id) { + return userRepository.findOneWithAuthoritiesById(id); + } + + @Transactional(readOnly = true) + public User getUserWithAuthorities() { + return userRepository.findOneWithAuthoritiesByLogin(SecurityUtils.getCurrentUserLogin()).orElse(null); + } + + + /** + * Not activated users should be automatically deleted after 3 days. + *

+ * This is scheduled to get fired everyday, at 01:00 (am). + *

+ */ + @Scheduled(cron = "0 0 1 * * ?") + public void removeNotActivatedUsers() { + ZonedDateTime now = ZonedDateTime.now(); + List users = userRepository.findAllByActivatedIsFalseAndCreatedDateBefore(now.minusDays(3)); + for (User user : users) { + log.debug("Deleting not activated user {}", user.getLogin()); + userRepository.delete(user); + } + } +} diff --git a/jhipster/src/main/java/com/baeldung/service/dto/UserDTO.java b/jhipster/src/main/java/com/baeldung/service/dto/UserDTO.java new file mode 100644 index 000000000000..1080bab7b81a --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/service/dto/UserDTO.java @@ -0,0 +1,167 @@ +package com.baeldung.service.dto; + +import com.baeldung.config.Constants; + +import com.baeldung.domain.Authority; +import com.baeldung.domain.User; + +import org.hibernate.validator.constraints.Email; + +import javax.validation.constraints.*; +import java.time.ZonedDateTime; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * A DTO representing a user, with his authorities. + */ +public class UserDTO { + + private Long id; + + @Pattern(regexp = Constants.LOGIN_REGEX) + @Size(min = 1, max = 50) + private String login; + + @Size(max = 50) + private String firstName; + + @Size(max = 50) + private String lastName; + + @Email + @Size(min = 5, max = 100) + private String email; + + @Size(max = 256) + private String imageUrl; + + private boolean activated = false; + + @Size(min = 2, max = 5) + private String langKey; + + private String createdBy; + + private ZonedDateTime createdDate; + + private String lastModifiedBy; + + private ZonedDateTime lastModifiedDate; + + private Set authorities; + + public UserDTO() { + // Empty constructor needed for MapStruct. + } + + public UserDTO(User user) { + this(user.getId(), user.getLogin(), user.getFirstName(), user.getLastName(), + user.getEmail(), user.getActivated(), user.getImageUrl(), user.getLangKey(), + user.getCreatedBy(), user.getCreatedDate(), user.getLastModifiedBy(), user.getLastModifiedDate(), + user.getAuthorities().stream().map(Authority::getName) + .collect(Collectors.toSet())); + } + + public UserDTO(Long id, String login, String firstName, String lastName, + String email, boolean activated, String imageUrl, String langKey, + String createdBy, ZonedDateTime createdDate, String lastModifiedBy, ZonedDateTime lastModifiedDate, + Set authorities) { + + this.id = id; + this.login = login; + this.firstName = firstName; + this.lastName = lastName; + this.email = email; + this.activated = activated; + this.imageUrl = imageUrl; + this.langKey = langKey; + this.createdBy = createdBy; + this.createdDate = createdDate; + this.lastModifiedBy = lastModifiedBy; + this.lastModifiedDate = lastModifiedDate; + this.authorities = authorities; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } + + public String getFirstName() { + return firstName; + } + + public String getLastName() { + return lastName; + } + + public String getEmail() { + return email; + } + + public String getImageUrl() { + return imageUrl; + } + + public boolean isActivated() { + return activated; + } + + public String getLangKey() { + return langKey; + } + + public String getCreatedBy() { + return createdBy; + } + + public ZonedDateTime getCreatedDate() { + return createdDate; + } + + public String getLastModifiedBy() { + return lastModifiedBy; + } + + public ZonedDateTime getLastModifiedDate() { + return lastModifiedDate; + } + + public void setLastModifiedDate(ZonedDateTime lastModifiedDate) { + this.lastModifiedDate = lastModifiedDate; + } + + public Set getAuthorities() { + return authorities; + } + + @Override + public String toString() { + return "UserDTO{" + + "login='" + login + '\'' + + ", firstName='" + firstName + '\'' + + ", lastName='" + lastName + '\'' + + ", email='" + email + '\'' + + ", imageUrl='" + imageUrl + '\'' + + ", activated=" + activated + + ", langKey='" + langKey + '\'' + + ", createdBy=" + createdBy + + ", createdDate=" + createdDate + + ", lastModifiedBy='" + lastModifiedBy + '\'' + + ", lastModifiedDate=" + lastModifiedDate + + ", authorities=" + authorities + + "}"; + } +} diff --git a/jhipster/src/main/java/com/baeldung/service/dto/package-info.java b/jhipster/src/main/java/com/baeldung/service/dto/package-info.java new file mode 100644 index 000000000000..12d048eb7a7e --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/service/dto/package-info.java @@ -0,0 +1,4 @@ +/** + * Data Transfer Objects. + */ +package com.baeldung.service.dto; diff --git a/jhipster/src/main/java/com/baeldung/service/mapper/UserMapper.java b/jhipster/src/main/java/com/baeldung/service/mapper/UserMapper.java new file mode 100644 index 000000000000..aaada37162e3 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/service/mapper/UserMapper.java @@ -0,0 +1,55 @@ +package com.baeldung.service.mapper; + +import com.baeldung.domain.Authority; +import com.baeldung.domain.User; +import com.baeldung.service.dto.UserDTO; +import org.mapstruct.*; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Mapper for the entity User and its DTO UserDTO. + */ +@Mapper(componentModel = "spring", uses = {}) +public interface UserMapper { + + UserDTO userToUserDTO(User user); + + List usersToUserDTOs(List users); + + @Mapping(target = "createdBy", ignore = true) + @Mapping(target = "createdDate", ignore = true) + @Mapping(target = "lastModifiedBy", ignore = true) + @Mapping(target = "lastModifiedDate", ignore = true) + @Mapping(target = "activationKey", ignore = true) + @Mapping(target = "resetKey", ignore = true) + @Mapping(target = "resetDate", ignore = true) + @Mapping(target = "password", ignore = true) + User userDTOToUser(UserDTO userDTO); + + List userDTOsToUsers(List userDTOs); + + default User userFromId(Long id) { + if (id == null) { + return null; + } + User user = new User(); + user.setId(id); + return user; + } + + default Set stringsFromAuthorities (Set authorities) { + return authorities.stream().map(Authority::getName) + .collect(Collectors.toSet()); + } + + default Set authoritiesFromStrings(Set strings) { + return strings.stream().map(string -> { + Authority auth = new Authority(); + auth.setName(string); + return auth; + }).collect(Collectors.toSet()); + } +} diff --git a/jhipster/src/main/java/com/baeldung/service/mapper/package-info.java b/jhipster/src/main/java/com/baeldung/service/mapper/package-info.java new file mode 100644 index 000000000000..75f8d24f08d1 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/service/mapper/package-info.java @@ -0,0 +1,4 @@ +/** + * MapStruct mappers for mapping domain objects and Data Transfer Objects. + */ +package com.baeldung.service.mapper; diff --git a/jhipster/src/main/java/com/baeldung/service/package-info.java b/jhipster/src/main/java/com/baeldung/service/package-info.java new file mode 100644 index 000000000000..0695bc147305 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/service/package-info.java @@ -0,0 +1,4 @@ +/** + * Service layer beans. + */ +package com.baeldung.service; diff --git a/jhipster/src/main/java/com/baeldung/service/util/RandomUtil.java b/jhipster/src/main/java/com/baeldung/service/util/RandomUtil.java new file mode 100644 index 000000000000..1f4d9f8b5d32 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/service/util/RandomUtil.java @@ -0,0 +1,41 @@ +package com.baeldung.service.util; + +import org.apache.commons.lang3.RandomStringUtils; + +/** + * Utility class for generating random Strings. + */ +public final class RandomUtil { + + private static final int DEF_COUNT = 20; + + private RandomUtil() { + } + + /** + * Generate a password. + * + * @return the generated password + */ + public static String generatePassword() { + return RandomStringUtils.randomAlphanumeric(DEF_COUNT); + } + + /** + * Generate an activation key. + * + * @return the generated activation key + */ + public static String generateActivationKey() { + return RandomStringUtils.randomNumeric(DEF_COUNT); + } + + /** + * Generate a reset key. + * + * @return the generated reset key + */ + public static String generateResetKey() { + return RandomStringUtils.randomNumeric(DEF_COUNT); + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/AccountResource.java b/jhipster/src/main/java/com/baeldung/web/rest/AccountResource.java new file mode 100644 index 000000000000..c1009fc9184a --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/AccountResource.java @@ -0,0 +1,202 @@ +package com.baeldung.web.rest; + +import com.codahale.metrics.annotation.Timed; + +import com.baeldung.domain.User; +import com.baeldung.repository.UserRepository; +import com.baeldung.security.SecurityUtils; +import com.baeldung.service.MailService; +import com.baeldung.service.UserService; +import com.baeldung.service.dto.UserDTO; +import com.baeldung.web.rest.vm.KeyAndPasswordVM; +import com.baeldung.web.rest.vm.ManagedUserVM; +import com.baeldung.web.rest.util.HeaderUtil; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import javax.validation.Valid; +import java.util.*; + +/** + * REST controller for managing the current user's account. + */ +@RestController +@RequestMapping("/api") +public class AccountResource { + + private final Logger log = LoggerFactory.getLogger(AccountResource.class); + + private final UserRepository userRepository; + + private final UserService userService; + + private final MailService mailService; + + public AccountResource(UserRepository userRepository, UserService userService, + MailService mailService) { + + this.userRepository = userRepository; + this.userService = userService; + this.mailService = mailService; + } + + /** + * POST /register : register the user. + * + * @param managedUserVM the managed user View Model + * @return the ResponseEntity with status 201 (Created) if the user is registered or 400 (Bad Request) if the login or e-mail is already in use + */ + @PostMapping(path = "/register", + produces={MediaType.APPLICATION_JSON_VALUE, MediaType.TEXT_PLAIN_VALUE}) + @Timed + public ResponseEntity registerAccount(@Valid @RequestBody ManagedUserVM managedUserVM) { + + HttpHeaders textPlainHeaders = new HttpHeaders(); + textPlainHeaders.setContentType(MediaType.TEXT_PLAIN); + + return userRepository.findOneByLogin(managedUserVM.getLogin().toLowerCase()) + .map(user -> new ResponseEntity<>("login already in use", textPlainHeaders, HttpStatus.BAD_REQUEST)) + .orElseGet(() -> userRepository.findOneByEmail(managedUserVM.getEmail()) + .map(user -> new ResponseEntity<>("e-mail address already in use", textPlainHeaders, HttpStatus.BAD_REQUEST)) + .orElseGet(() -> { + User user = userService + .createUser(managedUserVM.getLogin(), managedUserVM.getPassword(), + managedUserVM.getFirstName(), managedUserVM.getLastName(), + managedUserVM.getEmail().toLowerCase(), managedUserVM.getImageUrl(), managedUserVM.getLangKey()); + + mailService.sendActivationEmail(user); + return new ResponseEntity<>(HttpStatus.CREATED); + }) + ); + } + + /** + * GET /activate : activate the registered user. + * + * @param key the activation key + * @return the ResponseEntity with status 200 (OK) and the activated user in body, or status 500 (Internal Server Error) if the user couldn't be activated + */ + @GetMapping("/activate") + @Timed + public ResponseEntity activateAccount(@RequestParam(value = "key") String key) { + return userService.activateRegistration(key) + .map(user -> new ResponseEntity(HttpStatus.OK)) + .orElse(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR)); + } + + /** + * GET /authenticate : check if the user is authenticated, and return its login. + * + * @param request the HTTP request + * @return the login if the user is authenticated + */ + @GetMapping("/authenticate") + @Timed + public String isAuthenticated(HttpServletRequest request) { + log.debug("REST request to check if the current user is authenticated"); + return request.getRemoteUser(); + } + + /** + * GET /account : get the current user. + * + * @return the ResponseEntity with status 200 (OK) and the current user in body, or status 500 (Internal Server Error) if the user couldn't be returned + */ + @GetMapping("/account") + @Timed + public ResponseEntity getAccount() { + return Optional.ofNullable(userService.getUserWithAuthorities()) + .map(user -> new ResponseEntity<>(new UserDTO(user), HttpStatus.OK)) + .orElse(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR)); + } + + /** + * POST /account : update the current user information. + * + * @param userDTO the current user information + * @return the ResponseEntity with status 200 (OK), or status 400 (Bad Request) or 500 (Internal Server Error) if the user couldn't be updated + */ + @PostMapping("/account") + @Timed + public ResponseEntity saveAccount(@Valid @RequestBody UserDTO userDTO) { + Optional existingUser = userRepository.findOneByEmail(userDTO.getEmail()); + if (existingUser.isPresent() && (!existingUser.get().getLogin().equalsIgnoreCase(userDTO.getLogin()))) { + return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert("user-management", "emailexists", "Email already in use")).body(null); + } + return userRepository + .findOneByLogin(SecurityUtils.getCurrentUserLogin()) + .map(u -> { + userService.updateUser(userDTO.getFirstName(), userDTO.getLastName(), userDTO.getEmail(), + userDTO.getLangKey()); + return new ResponseEntity(HttpStatus.OK); + }) + .orElseGet(() -> new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR)); + } + + /** + * POST /account/change_password : changes the current user's password + * + * @param password the new password + * @return the ResponseEntity with status 200 (OK), or status 400 (Bad Request) if the new password is not strong enough + */ + @PostMapping(path = "/account/change_password", + produces = MediaType.TEXT_PLAIN_VALUE) + @Timed + public ResponseEntity changePassword(@RequestBody String password) { + if (!checkPasswordLength(password)) { + return new ResponseEntity<>("Incorrect password", HttpStatus.BAD_REQUEST); + } + userService.changePassword(password); + return new ResponseEntity<>(HttpStatus.OK); + } + + /** + * POST /account/reset_password/init : Send an e-mail to reset the password of the user + * + * @param mail the mail of the user + * @return the ResponseEntity with status 200 (OK) if the e-mail was sent, or status 400 (Bad Request) if the e-mail address is not registered + */ + @PostMapping(path = "/account/reset_password/init", + produces = MediaType.TEXT_PLAIN_VALUE) + @Timed + public ResponseEntity requestPasswordReset(@RequestBody String mail) { + return userService.requestPasswordReset(mail) + .map(user -> { + mailService.sendPasswordResetMail(user); + return new ResponseEntity<>("e-mail was sent", HttpStatus.OK); + }).orElse(new ResponseEntity<>("e-mail address not registered", HttpStatus.BAD_REQUEST)); + } + + /** + * POST /account/reset_password/finish : Finish to reset the password of the user + * + * @param keyAndPassword the generated key and the new password + * @return the ResponseEntity with status 200 (OK) if the password has been reset, + * or status 400 (Bad Request) or 500 (Internal Server Error) if the password could not be reset + */ + @PostMapping(path = "/account/reset_password/finish", + produces = MediaType.TEXT_PLAIN_VALUE) + @Timed + public ResponseEntity finishPasswordReset(@RequestBody KeyAndPasswordVM keyAndPassword) { + if (!checkPasswordLength(keyAndPassword.getNewPassword())) { + return new ResponseEntity<>("Incorrect password", HttpStatus.BAD_REQUEST); + } + return userService.completePasswordReset(keyAndPassword.getNewPassword(), keyAndPassword.getKey()) + .map(user -> new ResponseEntity(HttpStatus.OK)) + .orElse(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR)); + } + + private boolean checkPasswordLength(String password) { + return !StringUtils.isEmpty(password) && + password.length() >= ManagedUserVM.PASSWORD_MIN_LENGTH && + password.length() <= ManagedUserVM.PASSWORD_MAX_LENGTH; + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/AuditResource.java b/jhipster/src/main/java/com/baeldung/web/rest/AuditResource.java new file mode 100644 index 000000000000..3101d134ed8e --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/AuditResource.java @@ -0,0 +1,76 @@ +package com.baeldung.web.rest; + +import com.baeldung.service.AuditEventService; +import com.baeldung.web.rest.util.PaginationUtil; + +import io.github.jhipster.web.util.ResponseUtil; +import io.swagger.annotations.ApiParam; +import org.springframework.boot.actuate.audit.AuditEvent; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.net.URISyntaxException; +import java.time.LocalDate; +import java.util.List; + +/** + * REST controller for getting the audit events. + */ +@RestController +@RequestMapping("/management/audits") +public class AuditResource { + + private final AuditEventService auditEventService; + + public AuditResource(AuditEventService auditEventService) { + this.auditEventService = auditEventService; + } + + /** + * GET /audits : get a page of AuditEvents. + * + * @param pageable the pagination information + * @return the ResponseEntity with status 200 (OK) and the list of AuditEvents in body + */ + @GetMapping + public ResponseEntity> getAll(@ApiParam Pageable pageable) { + Page page = auditEventService.findAll(pageable); + HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/management/audits"); + return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK); + } + + /** + * GET /audits : get a page of AuditEvents between the fromDate and toDate. + * + * @param fromDate the start of the time period of AuditEvents to get + * @param toDate the end of the time period of AuditEvents to get + * @param pageable the pagination information + * @return the ResponseEntity with status 200 (OK) and the list of AuditEvents in body + */ + + @GetMapping(params = {"fromDate", "toDate"}) + public ResponseEntity> getByDates( + @RequestParam(value = "fromDate") LocalDate fromDate, + @RequestParam(value = "toDate") LocalDate toDate, + @ApiParam Pageable pageable) { + + Page page = auditEventService.findByDates(fromDate.atTime(0, 0), toDate.atTime(23, 59), pageable); + HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/management/audits"); + return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK); + } + + /** + * GET /audits/:id : get an AuditEvent by id. + * + * @param id the id of the entity to get + * @return the ResponseEntity with status 200 (OK) and the AuditEvent in body, or status 404 (Not Found) + */ + @GetMapping("/{id:.+}") + public ResponseEntity get(@PathVariable Long id) { + return ResponseUtil.wrapOrNotFound(auditEventService.find(id)); + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/CommentResource.java b/jhipster/src/main/java/com/baeldung/web/rest/CommentResource.java new file mode 100644 index 000000000000..3e3f13b5e98a --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/CommentResource.java @@ -0,0 +1,129 @@ +package com.baeldung.web.rest; + +import com.codahale.metrics.annotation.Timed; +import com.baeldung.domain.Comment; + +import com.baeldung.repository.CommentRepository; +import com.baeldung.web.rest.util.HeaderUtil; +import com.baeldung.web.rest.util.PaginationUtil; +import io.swagger.annotations.ApiParam; +import io.github.jhipster.web.util.ResponseUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; +import java.util.Optional; + +/** + * REST controller for managing Comment. + */ +@RestController +@RequestMapping("/api") +public class CommentResource { + + private final Logger log = LoggerFactory.getLogger(CommentResource.class); + + private static final String ENTITY_NAME = "comment"; + + private final CommentRepository commentRepository; + + public CommentResource(CommentRepository commentRepository) { + this.commentRepository = commentRepository; + } + + /** + * POST /comments : Create a new comment. + * + * @param comment the comment to create + * @return the ResponseEntity with status 201 (Created) and with body the new comment, or with status 400 (Bad Request) if the comment has already an ID + * @throws URISyntaxException if the Location URI syntax is incorrect + */ + @PostMapping("/comments") + @Timed + public ResponseEntity createComment(@Valid @RequestBody Comment comment) throws URISyntaxException { + log.debug("REST request to save Comment : {}", comment); + if (comment.getId() != null) { + return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert(ENTITY_NAME, "idexists", "A new comment cannot already have an ID")).body(null); + } + Comment result = commentRepository.save(comment); + return ResponseEntity.created(new URI("/api/comments/" + result.getId())) + .headers(HeaderUtil.createEntityCreationAlert(ENTITY_NAME, result.getId().toString())) + .body(result); + } + + /** + * PUT /comments : Updates an existing comment. + * + * @param comment the comment to update + * @return the ResponseEntity with status 200 (OK) and with body the updated comment, + * or with status 400 (Bad Request) if the comment is not valid, + * or with status 500 (Internal Server Error) if the comment couldnt be updated + * @throws URISyntaxException if the Location URI syntax is incorrect + */ + @PutMapping("/comments") + @Timed + public ResponseEntity updateComment(@Valid @RequestBody Comment comment) throws URISyntaxException { + log.debug("REST request to update Comment : {}", comment); + if (comment.getId() == null) { + return createComment(comment); + } + Comment result = commentRepository.save(comment); + return ResponseEntity.ok() + .headers(HeaderUtil.createEntityUpdateAlert(ENTITY_NAME, comment.getId().toString())) + .body(result); + } + + /** + * GET /comments : get all the comments. + * + * @param pageable the pagination information + * @return the ResponseEntity with status 200 (OK) and the list of comments in body + * @throws URISyntaxException if there is an error to generate the pagination HTTP headers + */ + @GetMapping("/comments") + @Timed + public ResponseEntity> getAllComments(@ApiParam Pageable pageable) { + log.debug("REST request to get a page of Comments"); + Page page = commentRepository.findAll(pageable); + HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/comments"); + return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK); + } + + /** + * GET /comments/:id : get the "id" comment. + * + * @param id the id of the comment to retrieve + * @return the ResponseEntity with status 200 (OK) and with body the comment, or with status 404 (Not Found) + */ + @GetMapping("/comments/{id}") + @Timed + public ResponseEntity getComment(@PathVariable Long id) { + log.debug("REST request to get Comment : {}", id); + Comment comment = commentRepository.findOne(id); + return ResponseUtil.wrapOrNotFound(Optional.ofNullable(comment)); + } + + /** + * DELETE /comments/:id : delete the "id" comment. + * + * @param id the id of the comment to delete + * @return the ResponseEntity with status 200 (OK) + */ + @DeleteMapping("/comments/{id}") + @Timed + public ResponseEntity deleteComment(@PathVariable Long id) { + log.debug("REST request to delete Comment : {}", id); + commentRepository.delete(id); + return ResponseEntity.ok().headers(HeaderUtil.createEntityDeletionAlert(ENTITY_NAME, id.toString())).build(); + } + +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/JWTToken.java b/jhipster/src/main/java/com/baeldung/web/rest/JWTToken.java new file mode 100644 index 000000000000..c0804851f318 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/JWTToken.java @@ -0,0 +1,24 @@ +package com.baeldung.web.rest; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Object to return as body in JWT Authentication. + */ +public class JWTToken { + + private String idToken; + + public JWTToken(String idToken) { + this.idToken = idToken; + } + + @JsonProperty("id_token") + public String getIdToken() { + return idToken; + } + + public void setIdToken(String idToken) { + this.idToken = idToken; + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/LogsResource.java b/jhipster/src/main/java/com/baeldung/web/rest/LogsResource.java new file mode 100644 index 000000000000..3d556e2609fb --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/LogsResource.java @@ -0,0 +1,39 @@ +package com.baeldung.web.rest; + +import com.baeldung.web.rest.vm.LoggerVM; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.LoggerContext; +import com.codahale.metrics.annotation.Timed; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * Controller for view and managing Log Level at runtime. + */ +@RestController +@RequestMapping("/management") +public class LogsResource { + + @GetMapping("/logs") + @Timed + public List getList() { + LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); + return context.getLoggerList() + .stream() + .map(LoggerVM::new) + .collect(Collectors.toList()); + } + + @PutMapping("/logs") + @ResponseStatus(HttpStatus.NO_CONTENT) + @Timed + public void changeLevel(@RequestBody LoggerVM jsonLogger) { + LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); + context.getLogger(jsonLogger.getName()).setLevel(Level.valueOf(jsonLogger.getLevel())); + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/PostResource.java b/jhipster/src/main/java/com/baeldung/web/rest/PostResource.java new file mode 100644 index 000000000000..7505e1fa468d --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/PostResource.java @@ -0,0 +1,129 @@ +package com.baeldung.web.rest; + +import com.codahale.metrics.annotation.Timed; +import com.baeldung.domain.Post; + +import com.baeldung.repository.PostRepository; +import com.baeldung.web.rest.util.HeaderUtil; +import com.baeldung.web.rest.util.PaginationUtil; +import io.swagger.annotations.ApiParam; +import io.github.jhipster.web.util.ResponseUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; +import java.util.Optional; + +/** + * REST controller for managing Post. + */ +@RestController +@RequestMapping("/api") +public class PostResource { + + private final Logger log = LoggerFactory.getLogger(PostResource.class); + + private static final String ENTITY_NAME = "post"; + + private final PostRepository postRepository; + + public PostResource(PostRepository postRepository) { + this.postRepository = postRepository; + } + + /** + * POST /posts : Create a new post. + * + * @param post the post to create + * @return the ResponseEntity with status 201 (Created) and with body the new post, or with status 400 (Bad Request) if the post has already an ID + * @throws URISyntaxException if the Location URI syntax is incorrect + */ + @PostMapping("/posts") + @Timed + public ResponseEntity createPost(@Valid @RequestBody Post post) throws URISyntaxException { + log.debug("REST request to save Post : {}", post); + if (post.getId() != null) { + return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert(ENTITY_NAME, "idexists", "A new post cannot already have an ID")).body(null); + } + Post result = postRepository.save(post); + return ResponseEntity.created(new URI("/api/posts/" + result.getId())) + .headers(HeaderUtil.createEntityCreationAlert(ENTITY_NAME, result.getId().toString())) + .body(result); + } + + /** + * PUT /posts : Updates an existing post. + * + * @param post the post to update + * @return the ResponseEntity with status 200 (OK) and with body the updated post, + * or with status 400 (Bad Request) if the post is not valid, + * or with status 500 (Internal Server Error) if the post couldnt be updated + * @throws URISyntaxException if the Location URI syntax is incorrect + */ + @PutMapping("/posts") + @Timed + public ResponseEntity updatePost(@Valid @RequestBody Post post) throws URISyntaxException { + log.debug("REST request to update Post : {}", post); + if (post.getId() == null) { + return createPost(post); + } + Post result = postRepository.save(post); + return ResponseEntity.ok() + .headers(HeaderUtil.createEntityUpdateAlert(ENTITY_NAME, post.getId().toString())) + .body(result); + } + + /** + * GET /posts : get all the posts. + * + * @param pageable the pagination information + * @return the ResponseEntity with status 200 (OK) and the list of posts in body + * @throws URISyntaxException if there is an error to generate the pagination HTTP headers + */ + @GetMapping("/posts") + @Timed + public ResponseEntity> getAllPosts(@ApiParam Pageable pageable) { + log.debug("REST request to get a page of Posts"); + Page page = postRepository.findAll(pageable); + HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/posts"); + return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK); + } + + /** + * GET /posts/:id : get the "id" post. + * + * @param id the id of the post to retrieve + * @return the ResponseEntity with status 200 (OK) and with body the post, or with status 404 (Not Found) + */ + @GetMapping("/posts/{id}") + @Timed + public ResponseEntity getPost(@PathVariable Long id) { + log.debug("REST request to get Post : {}", id); + Post post = postRepository.findOne(id); + return ResponseUtil.wrapOrNotFound(Optional.ofNullable(post)); + } + + /** + * DELETE /posts/:id : delete the "id" post. + * + * @param id the id of the post to delete + * @return the ResponseEntity with status 200 (OK) + */ + @DeleteMapping("/posts/{id}") + @Timed + public ResponseEntity deletePost(@PathVariable Long id) { + log.debug("REST request to delete Post : {}", id); + postRepository.delete(id); + return ResponseEntity.ok().headers(HeaderUtil.createEntityDeletionAlert(ENTITY_NAME, id.toString())).build(); + } + +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/ProfileInfoResource.java b/jhipster/src/main/java/com/baeldung/web/rest/ProfileInfoResource.java new file mode 100644 index 000000000000..b5e26c828137 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/ProfileInfoResource.java @@ -0,0 +1,69 @@ +package com.baeldung.web.rest; + +import com.baeldung.config.DefaultProfileUtil; + +import io.github.jhipster.config.JHipsterProperties; + +import org.springframework.core.env.Environment; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Resource to return information about the currently running Spring profiles. + */ +@RestController +@RequestMapping("/api") +public class ProfileInfoResource { + + private final Environment env; + + private final JHipsterProperties jHipsterProperties; + + public ProfileInfoResource(Environment env, JHipsterProperties jHipsterProperties) { + this.env = env; + this.jHipsterProperties = jHipsterProperties; + } + + @GetMapping("/profile-info") + public ProfileInfoVM getActiveProfiles() { + String[] activeProfiles = DefaultProfileUtil.getActiveProfiles(env); + return new ProfileInfoVM(activeProfiles, getRibbonEnv(activeProfiles)); + } + + private String getRibbonEnv(String[] activeProfiles) { + String[] displayOnActiveProfiles = jHipsterProperties.getRibbon().getDisplayOnActiveProfiles(); + if (displayOnActiveProfiles == null) { + return null; + } + List ribbonProfiles = new ArrayList<>(Arrays.asList(displayOnActiveProfiles)); + List springBootProfiles = Arrays.asList(activeProfiles); + ribbonProfiles.retainAll(springBootProfiles); + if (!ribbonProfiles.isEmpty()) { + return ribbonProfiles.get(0); + } + return null; + } + + class ProfileInfoVM { + + private String[] activeProfiles; + + private String ribbonEnv; + + ProfileInfoVM(String[] activeProfiles, String ribbonEnv) { + this.activeProfiles = activeProfiles; + this.ribbonEnv = ribbonEnv; + } + + public String[] getActiveProfiles() { + return activeProfiles; + } + + public String getRibbonEnv() { + return ribbonEnv; + } + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/UserJWTController.java b/jhipster/src/main/java/com/baeldung/web/rest/UserJWTController.java new file mode 100644 index 000000000000..02b77c51a6aa --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/UserJWTController.java @@ -0,0 +1,60 @@ +package com.baeldung.web.rest; + +import com.baeldung.security.jwt.JWTConfigurer; +import com.baeldung.security.jwt.TokenProvider; +import com.baeldung.web.rest.vm.LoginVM; + +import java.util.Collections; + +import com.codahale.metrics.annotation.Timed; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; + +@RestController +@RequestMapping("/api") +public class UserJWTController { + + private final Logger log = LoggerFactory.getLogger(UserJWTController.class); + + private final TokenProvider tokenProvider; + + private final AuthenticationManager authenticationManager; + + public UserJWTController(TokenProvider tokenProvider, AuthenticationManager authenticationManager) { + this.tokenProvider = tokenProvider; + this.authenticationManager = authenticationManager; + } + + @PostMapping("/authenticate") + @Timed + public ResponseEntity authorize(@Valid @RequestBody LoginVM loginVM, HttpServletResponse response) { + + UsernamePasswordAuthenticationToken authenticationToken = + new UsernamePasswordAuthenticationToken(loginVM.getUsername(), loginVM.getPassword()); + + try { + Authentication authentication = this.authenticationManager.authenticate(authenticationToken); + SecurityContextHolder.getContext().setAuthentication(authentication); + boolean rememberMe = (loginVM.isRememberMe() == null) ? false : loginVM.isRememberMe(); + String jwt = tokenProvider.createToken(authentication, rememberMe); + response.addHeader(JWTConfigurer.AUTHORIZATION_HEADER, "Bearer " + jwt); + return ResponseEntity.ok(new JWTToken(jwt)); + } catch (AuthenticationException ae) { + log.trace("Authentication exception trace: {}", ae); + return new ResponseEntity<>(Collections.singletonMap("AuthenticationException", + ae.getLocalizedMessage()), HttpStatus.UNAUTHORIZED); + } + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/UserResource.java b/jhipster/src/main/java/com/baeldung/web/rest/UserResource.java new file mode 100644 index 000000000000..296d3e30ba45 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/UserResource.java @@ -0,0 +1,187 @@ +package com.baeldung.web.rest; + +import com.baeldung.config.Constants; +import com.codahale.metrics.annotation.Timed; +import com.baeldung.domain.User; +import com.baeldung.repository.UserRepository; +import com.baeldung.security.AuthoritiesConstants; +import com.baeldung.service.MailService; +import com.baeldung.service.UserService; +import com.baeldung.service.dto.UserDTO; +import com.baeldung.web.rest.vm.ManagedUserVM; +import com.baeldung.web.rest.util.HeaderUtil; +import com.baeldung.web.rest.util.PaginationUtil; +import io.github.jhipster.web.util.ResponseUtil; +import io.swagger.annotations.ApiParam; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.annotation.Secured; +import org.springframework.web.bind.annotation.*; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.*; + +/** + * REST controller for managing users. + * + *

This class accesses the User entity, and needs to fetch its collection of authorities.

+ *

+ * For a normal use-case, it would be better to have an eager relationship between User and Authority, + * and send everything to the client side: there would be no View Model and DTO, a lot less code, and an outer-join + * which would be good for performance. + *

+ *

+ * We use a View Model and a DTO for 3 reasons: + *

    + *
  • We want to keep a lazy association between the user and the authorities, because people will + * quite often do relationships with the user, and we don't want them to get the authorities all + * the time for nothing (for performance reasons). This is the #1 goal: we should not impact our users' + * application because of this use-case.
  • + *
  • Not having an outer join causes n+1 requests to the database. This is not a real issue as + * we have by default a second-level cache. This means on the first HTTP call we do the n+1 requests, + * but then all authorities come from the cache, so in fact it's much better than doing an outer join + * (which will get lots of data from the database, for each HTTP call).
  • + *
  • As this manages users, for security reasons, we'd rather have a DTO layer.
  • + *
+ *

Another option would be to have a specific JPA entity graph to handle this case.

+ */ +@RestController +@RequestMapping("/api") +public class UserResource { + + private final Logger log = LoggerFactory.getLogger(UserResource.class); + + private static final String ENTITY_NAME = "userManagement"; + + private final UserRepository userRepository; + + private final MailService mailService; + + private final UserService userService; + + public UserResource(UserRepository userRepository, MailService mailService, + UserService userService) { + + this.userRepository = userRepository; + this.mailService = mailService; + this.userService = userService; + } + + /** + * POST /users : Creates a new user. + *

+ * Creates a new user if the login and email are not already used, and sends an + * mail with an activation link. + * The user needs to be activated on creation. + *

+ * + * @param managedUserVM the user to create + * @return the ResponseEntity with status 201 (Created) and with body the new user, or with status 400 (Bad Request) if the login or email is already in use + * @throws URISyntaxException if the Location URI syntax is incorrect + */ + @PostMapping("/users") + @Timed + @Secured(AuthoritiesConstants.ADMIN) + public ResponseEntity createUser(@RequestBody ManagedUserVM managedUserVM) throws URISyntaxException { + log.debug("REST request to save User : {}", managedUserVM); + + if (managedUserVM.getId() != null) { + return ResponseEntity.badRequest() + .headers(HeaderUtil.createFailureAlert(ENTITY_NAME, "idexists", "A new user cannot already have an ID")) + .body(null); + // Lowercase the user login before comparing with database + } else if (userRepository.findOneByLogin(managedUserVM.getLogin().toLowerCase()).isPresent()) { + return ResponseEntity.badRequest() + .headers(HeaderUtil.createFailureAlert(ENTITY_NAME, "userexists", "Login already in use")) + .body(null); + } else if (userRepository.findOneByEmail(managedUserVM.getEmail()).isPresent()) { + return ResponseEntity.badRequest() + .headers(HeaderUtil.createFailureAlert(ENTITY_NAME, "emailexists", "Email already in use")) + .body(null); + } else { + User newUser = userService.createUser(managedUserVM); + mailService.sendCreationEmail(newUser); + return ResponseEntity.created(new URI("/api/users/" + newUser.getLogin())) + .headers(HeaderUtil.createAlert( "userManagement.created", newUser.getLogin())) + .body(newUser); + } + } + + /** + * PUT /users : Updates an existing User. + * + * @param managedUserVM the user to update + * @return the ResponseEntity with status 200 (OK) and with body the updated user, + * or with status 400 (Bad Request) if the login or email is already in use, + * or with status 500 (Internal Server Error) if the user couldn't be updated + */ + @PutMapping("/users") + @Timed + @Secured(AuthoritiesConstants.ADMIN) + public ResponseEntity updateUser(@RequestBody ManagedUserVM managedUserVM) { + log.debug("REST request to update User : {}", managedUserVM); + Optional existingUser = userRepository.findOneByEmail(managedUserVM.getEmail()); + if (existingUser.isPresent() && (!existingUser.get().getId().equals(managedUserVM.getId()))) { + return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert(ENTITY_NAME, "emailexists", "E-mail already in use")).body(null); + } + existingUser = userRepository.findOneByLogin(managedUserVM.getLogin().toLowerCase()); + if (existingUser.isPresent() && (!existingUser.get().getId().equals(managedUserVM.getId()))) { + return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert(ENTITY_NAME, "userexists", "Login already in use")).body(null); + } + Optional updatedUser = userService.updateUser(managedUserVM); + + return ResponseUtil.wrapOrNotFound(updatedUser, + HeaderUtil.createAlert("userManagement.updated", managedUserVM.getLogin())); + } + + /** + * GET /users : get all users. + * + * @param pageable the pagination information + * @return the ResponseEntity with status 200 (OK) and with body all users + */ + @GetMapping("/users") + @Timed + public ResponseEntity> getAllUsers(@ApiParam Pageable pageable) { + final Page page = userService.getAllManagedUsers(pageable); + HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/users"); + return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK); + } + + /** + * GET /users/:login : get the "login" user. + * + * @param login the login of the user to find + * @return the ResponseEntity with status 200 (OK) and with body the "login" user, or with status 404 (Not Found) + */ + @GetMapping("/users/{login:" + Constants.LOGIN_REGEX + "}") + @Timed + public ResponseEntity getUser(@PathVariable String login) { + log.debug("REST request to get User : {}", login); + return ResponseUtil.wrapOrNotFound( + userService.getUserWithAuthoritiesByLogin(login) + .map(UserDTO::new)); + } + + /** + * DELETE /users/:login : delete the "login" User. + * + * @param login the login of the user to delete + * @return the ResponseEntity with status 200 (OK) + */ + @DeleteMapping("/users/{login:" + Constants.LOGIN_REGEX + "}") + @Timed + @Secured(AuthoritiesConstants.ADMIN) + public ResponseEntity deleteUser(@PathVariable String login) { + log.debug("REST request to delete User: {}", login); + userService.deleteUser(login); + return ResponseEntity.ok().headers(HeaderUtil.createAlert( "userManagement.deleted", login)).build(); + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/errors/CustomParameterizedException.java b/jhipster/src/main/java/com/baeldung/web/rest/errors/CustomParameterizedException.java new file mode 100644 index 000000000000..ab7754476baf --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/errors/CustomParameterizedException.java @@ -0,0 +1,34 @@ +package com.baeldung.web.rest.errors; + +/** + * Custom, parameterized exception, which can be translated on the client side. + * For example: + * + *
+ * throw new CustomParameterizedException("myCustomError", "hello", "world");
+ * 
+ * + * Can be translated with: + * + *
+ * "error.myCustomError" :  "The server says {{params[0]}} to {{params[1]}}"
+ * 
+ */ +public class CustomParameterizedException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + private final String message; + private final String[] params; + + public CustomParameterizedException(String message, String... params) { + super(message); + this.message = message; + this.params = params; + } + + public ParameterizedErrorVM getErrorVM() { + return new ParameterizedErrorVM(message, params); + } + +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/errors/ErrorConstants.java b/jhipster/src/main/java/com/baeldung/web/rest/errors/ErrorConstants.java new file mode 100644 index 000000000000..69f21ed96c4b --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/errors/ErrorConstants.java @@ -0,0 +1,14 @@ +package com.baeldung.web.rest.errors; + +public final class ErrorConstants { + + public static final String ERR_CONCURRENCY_FAILURE = "error.concurrencyFailure"; + public static final String ERR_ACCESS_DENIED = "error.accessDenied"; + public static final String ERR_VALIDATION = "error.validation"; + public static final String ERR_METHOD_NOT_SUPPORTED = "error.methodNotSupported"; + public static final String ERR_INTERNAL_SERVER_ERROR = "error.internalServerError"; + + private ErrorConstants() { + } + +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/errors/ErrorVM.java b/jhipster/src/main/java/com/baeldung/web/rest/errors/ErrorVM.java new file mode 100644 index 000000000000..22fb06613588 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/errors/ErrorVM.java @@ -0,0 +1,52 @@ +package com.baeldung.web.rest.errors; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * View Model for transferring error message with a list of field errors. + */ +public class ErrorVM implements Serializable { + + private static final long serialVersionUID = 1L; + + private final String message; + private final String description; + + private List fieldErrors; + + public ErrorVM(String message) { + this(message, null); + } + + public ErrorVM(String message, String description) { + this.message = message; + this.description = description; + } + + public ErrorVM(String message, String description, List fieldErrors) { + this.message = message; + this.description = description; + this.fieldErrors = fieldErrors; + } + + public void add(String objectName, String field, String message) { + if (fieldErrors == null) { + fieldErrors = new ArrayList<>(); + } + fieldErrors.add(new FieldErrorVM(objectName, field, message)); + } + + public String getMessage() { + return message; + } + + public String getDescription() { + return description; + } + + public List getFieldErrors() { + return fieldErrors; + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/errors/ExceptionTranslator.java b/jhipster/src/main/java/com/baeldung/web/rest/errors/ExceptionTranslator.java new file mode 100644 index 000000000000..51925bfa6153 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/errors/ExceptionTranslator.java @@ -0,0 +1,85 @@ +package com.baeldung.web.rest.errors; + +import java.util.List; + +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.dao.ConcurrencyFailureException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.http.ResponseEntity.BodyBuilder; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.validation.BindingResult; +import org.springframework.validation.FieldError; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.*; + +/** + * Controller advice to translate the server side exceptions to client-friendly json structures. + */ +@ControllerAdvice +public class ExceptionTranslator { + + @ExceptionHandler(ConcurrencyFailureException.class) + @ResponseStatus(HttpStatus.CONFLICT) + @ResponseBody + public ErrorVM processConcurrencyError(ConcurrencyFailureException ex) { + return new ErrorVM(ErrorConstants.ERR_CONCURRENCY_FAILURE); + } + + @ExceptionHandler(MethodArgumentNotValidException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ResponseBody + public ErrorVM processValidationError(MethodArgumentNotValidException ex) { + BindingResult result = ex.getBindingResult(); + List fieldErrors = result.getFieldErrors(); + + return processFieldErrors(fieldErrors); + } + + @ExceptionHandler(CustomParameterizedException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ResponseBody + public ParameterizedErrorVM processParameterizedValidationError(CustomParameterizedException ex) { + return ex.getErrorVM(); + } + + @ExceptionHandler(AccessDeniedException.class) + @ResponseStatus(HttpStatus.FORBIDDEN) + @ResponseBody + public ErrorVM processAccessDeniedException(AccessDeniedException e) { + return new ErrorVM(ErrorConstants.ERR_ACCESS_DENIED, e.getMessage()); + } + + private ErrorVM processFieldErrors(List fieldErrors) { + ErrorVM dto = new ErrorVM(ErrorConstants.ERR_VALIDATION); + + for (FieldError fieldError : fieldErrors) { + dto.add(fieldError.getObjectName(), fieldError.getField(), fieldError.getCode()); + } + + return dto; + } + + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + @ResponseBody + @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED) + public ErrorVM processMethodNotSupportedException(HttpRequestMethodNotSupportedException exception) { + return new ErrorVM(ErrorConstants.ERR_METHOD_NOT_SUPPORTED, exception.getMessage()); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity processRuntimeException(Exception ex) { + BodyBuilder builder; + ErrorVM errorVM; + ResponseStatus responseStatus = AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class); + if (responseStatus != null) { + builder = ResponseEntity.status(responseStatus.value()); + errorVM = new ErrorVM("error." + responseStatus.value().value(), responseStatus.reason()); + } else { + builder = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR); + errorVM = new ErrorVM(ErrorConstants.ERR_INTERNAL_SERVER_ERROR, "Internal server error"); + } + return builder.body(errorVM); + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/errors/FieldErrorVM.java b/jhipster/src/main/java/com/baeldung/web/rest/errors/FieldErrorVM.java new file mode 100644 index 000000000000..19ab640ec7e0 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/errors/FieldErrorVM.java @@ -0,0 +1,33 @@ +package com.baeldung.web.rest.errors; + +import java.io.Serializable; + +public class FieldErrorVM implements Serializable { + + private static final long serialVersionUID = 1L; + + private final String objectName; + + private final String field; + + private final String message; + + public FieldErrorVM(String dto, String field, String message) { + this.objectName = dto; + this.field = field; + this.message = message; + } + + public String getObjectName() { + return objectName; + } + + public String getField() { + return field; + } + + public String getMessage() { + return message; + } + +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/errors/ParameterizedErrorVM.java b/jhipster/src/main/java/com/baeldung/web/rest/errors/ParameterizedErrorVM.java new file mode 100644 index 000000000000..18500c51af53 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/errors/ParameterizedErrorVM.java @@ -0,0 +1,27 @@ +package com.baeldung.web.rest.errors; + +import java.io.Serializable; + +/** + * View Model for sending a parameterized error message. + */ +public class ParameterizedErrorVM implements Serializable { + + private static final long serialVersionUID = 1L; + private final String message; + private final String[] params; + + public ParameterizedErrorVM(String message, String... params) { + this.message = message; + this.params = params; + } + + public String getMessage() { + return message; + } + + public String[] getParams() { + return params; + } + +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/package-info.java b/jhipster/src/main/java/com/baeldung/web/rest/package-info.java new file mode 100644 index 000000000000..0a74f6e90c48 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/package-info.java @@ -0,0 +1,4 @@ +/** + * Spring MVC REST controllers. + */ +package com.baeldung.web.rest; diff --git a/jhipster/src/main/java/com/baeldung/web/rest/util/HeaderUtil.java b/jhipster/src/main/java/com/baeldung/web/rest/util/HeaderUtil.java new file mode 100644 index 000000000000..7c643e9323b8 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/util/HeaderUtil.java @@ -0,0 +1,45 @@ +package com.baeldung.web.rest.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; + +/** + * Utility class for HTTP headers creation. + */ +public final class HeaderUtil { + + private static final Logger log = LoggerFactory.getLogger(HeaderUtil.class); + + private static final String APPLICATION_NAME = "baeldungApp"; + + private HeaderUtil() { + } + + public static HttpHeaders createAlert(String message, String param) { + HttpHeaders headers = new HttpHeaders(); + headers.add("X-baeldungApp-alert", message); + headers.add("X-baeldungApp-params", param); + return headers; + } + + public static HttpHeaders createEntityCreationAlert(String entityName, String param) { + return createAlert(APPLICATION_NAME + "." + entityName + ".created", param); + } + + public static HttpHeaders createEntityUpdateAlert(String entityName, String param) { + return createAlert(APPLICATION_NAME + "." + entityName + ".updated", param); + } + + public static HttpHeaders createEntityDeletionAlert(String entityName, String param) { + return createAlert(APPLICATION_NAME + "." + entityName + ".deleted", param); + } + + public static HttpHeaders createFailureAlert(String entityName, String errorKey, String defaultMessage) { + log.error("Entity creation failed, {}", defaultMessage); + HttpHeaders headers = new HttpHeaders(); + headers.add("X-baeldungApp-error", "error." + errorKey); + headers.add("X-baeldungApp-params", entityName); + return headers; + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/util/PaginationUtil.java b/jhipster/src/main/java/com/baeldung/web/rest/util/PaginationUtil.java new file mode 100644 index 000000000000..affcb7842c48 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/util/PaginationUtil.java @@ -0,0 +1,47 @@ +package com.baeldung.web.rest.util; + +import org.springframework.data.domain.Page; +import org.springframework.http.HttpHeaders; +import org.springframework.web.util.UriComponentsBuilder; + +import java.net.URISyntaxException; + +/** + * Utility class for handling pagination. + * + *

+ * Pagination uses the same principles as the Github API, + * and follow RFC 5988 (Link header). + */ +public final class PaginationUtil { + + private PaginationUtil() { + } + + public static HttpHeaders generatePaginationHttpHeaders(Page page, String baseUrl) { + + HttpHeaders headers = new HttpHeaders(); + headers.add("X-Total-Count", "" + Long.toString(page.getTotalElements())); + String link = ""; + if ((page.getNumber() + 1) < page.getTotalPages()) { + link = "<" + generateUri(baseUrl, page.getNumber() + 1, page.getSize()) + ">; rel=\"next\","; + } + // prev link + if ((page.getNumber()) > 0) { + link += "<" + generateUri(baseUrl, page.getNumber() - 1, page.getSize()) + ">; rel=\"prev\","; + } + // last and first link + int lastPage = 0; + if (page.getTotalPages() > 0) { + lastPage = page.getTotalPages() - 1; + } + link += "<" + generateUri(baseUrl, lastPage, page.getSize()) + ">; rel=\"last\","; + link += "<" + generateUri(baseUrl, 0, page.getSize()) + ">; rel=\"first\""; + headers.add(HttpHeaders.LINK, link); + return headers; + } + + private static String generateUri(String baseUrl, int page, int size) { + return UriComponentsBuilder.fromUriString(baseUrl).queryParam("page", page).queryParam("size", size).toUriString(); + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/vm/KeyAndPasswordVM.java b/jhipster/src/main/java/com/baeldung/web/rest/vm/KeyAndPasswordVM.java new file mode 100644 index 000000000000..94465f309fa9 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/vm/KeyAndPasswordVM.java @@ -0,0 +1,27 @@ +package com.baeldung.web.rest.vm; + +/** + * View Model object for storing the user's key and password. + */ +public class KeyAndPasswordVM { + + private String key; + + private String newPassword; + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getNewPassword() { + return newPassword; + } + + public void setNewPassword(String newPassword) { + this.newPassword = newPassword; + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/vm/LoggerVM.java b/jhipster/src/main/java/com/baeldung/web/rest/vm/LoggerVM.java new file mode 100644 index 000000000000..56f77aec85ab --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/vm/LoggerVM.java @@ -0,0 +1,48 @@ +package com.baeldung.web.rest.vm; + +import ch.qos.logback.classic.Logger; +import com.fasterxml.jackson.annotation.JsonCreator; + +/** + * View Model object for storing a Logback logger. + */ +public class LoggerVM { + + private String name; + + private String level; + + public LoggerVM(Logger logger) { + this.name = logger.getName(); + this.level = logger.getEffectiveLevel().toString(); + } + + @JsonCreator + public LoggerVM() { + // Empty public constructor used by Jackson. + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLevel() { + return level; + } + + public void setLevel(String level) { + this.level = level; + } + + @Override + public String toString() { + return "LoggerVM{" + + "name='" + name + '\'' + + ", level='" + level + '\'' + + '}'; + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/vm/LoginVM.java b/jhipster/src/main/java/com/baeldung/web/rest/vm/LoginVM.java new file mode 100644 index 000000000000..a910d0d59c85 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/vm/LoginVM.java @@ -0,0 +1,55 @@ +package com.baeldung.web.rest.vm; + +import com.baeldung.config.Constants; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; + +/** + * View Model object for storing a user's credentials. + */ +public class LoginVM { + + @Pattern(regexp = Constants.LOGIN_REGEX) + @NotNull + @Size(min = 1, max = 50) + private String username; + + @NotNull + @Size(min = ManagedUserVM.PASSWORD_MIN_LENGTH, max = ManagedUserVM.PASSWORD_MAX_LENGTH) + private String password; + + private Boolean rememberMe; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public Boolean isRememberMe() { + return rememberMe; + } + + public void setRememberMe(Boolean rememberMe) { + this.rememberMe = rememberMe; + } + + @Override + public String toString() { + return "LoginVM{" + + "username='" + username + '\'' + + ", rememberMe=" + rememberMe + + '}'; + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/vm/ManagedUserVM.java b/jhipster/src/main/java/com/baeldung/web/rest/vm/ManagedUserVM.java new file mode 100644 index 000000000000..0899f0d0aa6a --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/vm/ManagedUserVM.java @@ -0,0 +1,45 @@ +package com.baeldung.web.rest.vm; + +import com.baeldung.service.dto.UserDTO; +import javax.validation.constraints.Size; + +import java.time.ZonedDateTime; +import java.util.Set; + +/** + * View Model extending the UserDTO, which is meant to be used in the user management UI. + */ +public class ManagedUserVM extends UserDTO { + + public static final int PASSWORD_MIN_LENGTH = 4; + + public static final int PASSWORD_MAX_LENGTH = 100; + + @Size(min = PASSWORD_MIN_LENGTH, max = PASSWORD_MAX_LENGTH) + private String password; + + public ManagedUserVM() { + // Empty constructor needed for Jackson. + } + + public ManagedUserVM(Long id, String login, String password, String firstName, String lastName, + String email, boolean activated, String imageUrl, String langKey, + String createdBy, ZonedDateTime createdDate, String lastModifiedBy, ZonedDateTime lastModifiedDate, + Set authorities) { + + super(id, login, firstName, lastName, email, activated, imageUrl, langKey, + createdBy, createdDate, lastModifiedBy, lastModifiedDate, authorities); + + this.password = password; + } + + public String getPassword() { + return password; + } + + @Override + public String toString() { + return "ManagedUserVM{" + + "} " + super.toString(); + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/vm/package-info.java b/jhipster/src/main/java/com/baeldung/web/rest/vm/package-info.java new file mode 100644 index 000000000000..8d56157b6ac6 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/vm/package-info.java @@ -0,0 +1,4 @@ +/** + * View Models used by Spring MVC REST controllers. + */ +package com.baeldung.web.rest.vm; diff --git a/jhipster/src/main/resources/.h2.server.properties b/jhipster/src/main/resources/.h2.server.properties new file mode 100644 index 000000000000..f8b442990280 --- /dev/null +++ b/jhipster/src/main/resources/.h2.server.properties @@ -0,0 +1,5 @@ +#H2 Server Properties +0=JHipster H2 (Disk)|org.h2.Driver|jdbc\:h2\:file\:./target/h2db/db/baeldung|baeldung +webAllowOthers=true +webPort=8082 +webSSL=false diff --git a/jhipster/src/main/resources/banner.txt b/jhipster/src/main/resources/banner.txt new file mode 100644 index 000000000000..c3d8cf725ddc --- /dev/null +++ b/jhipster/src/main/resources/banner.txt @@ -0,0 +1,10 @@ + + ${AnsiColor.GREEN} ██╗${AnsiColor.RED} ██╗ ██╗ ████████╗ ███████╗ ██████╗ ████████╗ ████████╗ ███████╗ + ${AnsiColor.GREEN} ██║${AnsiColor.RED} ██║ ██║ ╚══██╔══╝ ██╔═══██╗ ██╔════╝ ╚══██╔══╝ ██╔═════╝ ██╔═══██╗ + ${AnsiColor.GREEN} ██║${AnsiColor.RED} ████████║ ██║ ███████╔╝ ╚█████╗ ██║ ██████╗ ███████╔╝ + ${AnsiColor.GREEN}██╗ ██║${AnsiColor.RED} ██╔═══██║ ██║ ██╔════╝ ╚═══██╗ ██║ ██╔═══╝ ██╔══██║ + ${AnsiColor.GREEN}╚██████╔╝${AnsiColor.RED} ██║ ██║ ████████╗ ██║ ██████╔╝ ██║ ████████╗ ██║ ╚██╗ + ${AnsiColor.GREEN} ╚═════╝ ${AnsiColor.RED} ╚═╝ ╚═╝ ╚═══════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══════╝ ╚═╝ ╚═╝ + +${AnsiColor.BRIGHT_BLUE}:: JHipster 🤓 :: Running Spring Boot ${spring-boot.version} :: +:: http://jhipster.github.io ::${AnsiColor.DEFAULT} diff --git a/jhipster/src/main/resources/config/application-dev.yml b/jhipster/src/main/resources/config/application-dev.yml new file mode 100644 index 000000000000..d75c2549c872 --- /dev/null +++ b/jhipster/src/main/resources/config/application-dev.yml @@ -0,0 +1,130 @@ +# =================================================================== +# Spring Boot configuration for the "dev" profile. +# +# This configuration overrides the application.yml file. +# +# More information on profiles: https://jhipster.github.io/profiles/ +# More information on configuration properties: https://jhipster.github.io/common-application-properties/ +# =================================================================== + +# =================================================================== +# Standard Spring Boot properties. +# Full reference is available at: +# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html +# =================================================================== + +spring: + profiles: + active: dev + include: swagger + devtools: + restart: + enabled: true + livereload: + enabled: false # we use gulp + BrowserSync for livereload + jackson: + serialization.indent_output: true + datasource: + type: com.zaxxer.hikari.HikariDataSource + url: jdbc:h2:file:./target/h2db/db/baeldung;DB_CLOSE_DELAY=-1 + username: baeldung + password: + h2: + console: + enabled: false + jpa: + database-platform: io.github.jhipster.domain.util.FixedH2Dialect + database: H2 + show-sql: true + properties: + hibernate.id.new_generator_mappings: true + hibernate.cache.use_second_level_cache: true + hibernate.cache.use_query_cache: false + hibernate.generate_statistics: true + hibernate.cache.region.factory_class: io.github.jhipster.config.jcache.NoDefaultJCacheRegionFactory + mail: + host: localhost + port: 25 + username: + password: + messages: + cache-seconds: 1 + thymeleaf: + cache: false + +liquibase: + contexts: dev + +# =================================================================== +# To enable SSL, generate a certificate using: +# keytool -genkey -alias baeldung -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650 +# +# You can also use Let's Encrypt: +# https://maximilian-boehm.com/hp2121/Create-a-Java-Keystore-JKS-from-Let-s-Encrypt-Certificates.htm +# +# Then, modify the server.ssl properties so your "server" configuration looks like: +# +# server: +# port: 8443 +# ssl: +# key-store: keystore.p12 +# key-store-password: +# keyStoreType: PKCS12 +# keyAlias: baeldung +# =================================================================== +server: + port: 8080 + +# =================================================================== +# JHipster specific properties +# +# Full reference is available at: https://jhipster.github.io/common-application-properties/ +# =================================================================== + +jhipster: + http: + version: V_1_1 # To use HTTP/2 you will need SSL support (see above the "server.ssl" configuration) + cache: # Cache configuration + ehcache: # Ehcache configuration + time-to-live-seconds: 3600 # By default objects stay 1 hour in the cache + max-entries: 100 # Number of objects in each cache entry + security: + authentication: + jwt: + secret: my-secret-token-to-change-in-production + # Token is valid 24 hours + token-validity-in-seconds: 86400 + token-validity-in-seconds-for-remember-me: 2592000 + mail: # specific JHipster mail property, for standard properties see MailProperties + from: baeldung@localhost + base-url: http://127.0.0.1:8080 + metrics: # DropWizard Metrics configuration, used by MetricsConfiguration + jmx.enabled: true + graphite: # Use the "graphite" Maven profile to have the Graphite dependencies + enabled: false + host: localhost + port: 2003 + prefix: baeldung + prometheus: # Use the "prometheus" Maven profile to have the Prometheus dependencies + enabled: false + endpoint: /prometheusMetrics + logs: # Reports Dropwizard metrics in the logs + enabled: false + reportFrequency: 60 # in seconds + logging: + logstash: # Forward logs to logstash over a socket, used by LoggingConfiguration + enabled: false + host: localhost + port: 5000 + queue-size: 512 + +# =================================================================== +# Application specific properties +# Add your own application properties here, see the ApplicationProperties class +# to have type-safe configuration, like in the JHipsterProperties above +# +# More documentation is available at: +# https://jhipster.github.io/common-application-properties/ +# =================================================================== + +application: diff --git a/jhipster/src/main/resources/config/application-prod.yml b/jhipster/src/main/resources/config/application-prod.yml new file mode 100644 index 000000000000..a46fbd1c7365 --- /dev/null +++ b/jhipster/src/main/resources/config/application-prod.yml @@ -0,0 +1,132 @@ +# =================================================================== +# Spring Boot configuration for the "prod" profile. +# +# This configuration overrides the application.yml file. +# +# More information on profiles: https://jhipster.github.io/profiles/ +# More information on configuration properties: https://jhipster.github.io/common-application-properties/ +# =================================================================== + +# =================================================================== +# Standard Spring Boot properties. +# Full reference is available at: +# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html +# =================================================================== + +spring: + devtools: + restart: + enabled: false + livereload: + enabled: false + datasource: + type: com.zaxxer.hikari.HikariDataSource + url: jdbc:mysql://localhost:3306/baeldung?useUnicode=true&characterEncoding=utf8&useSSL=false + username: root + password: + hikari: + data-source-properties: + cachePrepStmts: true + prepStmtCacheSize: 250 + prepStmtCacheSqlLimit: 2048 + useServerPrepStmts: true + jpa: + database-platform: org.hibernate.dialect.MySQL5InnoDBDialect + database: MYSQL + show-sql: false + properties: + hibernate.id.new_generator_mappings: true + hibernate.cache.use_second_level_cache: true + hibernate.cache.use_query_cache: false + hibernate.generate_statistics: false + hibernate.cache.region.factory_class: io.github.jhipster.config.jcache.NoDefaultJCacheRegionFactory + mail: + host: localhost + port: 25 + username: + password: + thymeleaf: + cache: true + +liquibase: + contexts: prod + +# =================================================================== +# To enable SSL, generate a certificate using: +# keytool -genkey -alias baeldung -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650 +# +# You can also use Let's Encrypt: +# https://maximilian-boehm.com/hp2121/Create-a-Java-Keystore-JKS-from-Let-s-Encrypt-Certificates.htm +# +# Then, modify the server.ssl properties so your "server" configuration looks like: +# +# server: +# port: 443 +# ssl: +# key-store: keystore.p12 +# key-store-password: +# keyStoreType: PKCS12 +# keyAlias: baeldung +# =================================================================== +server: + port: 8080 + compression: + enabled: true + mime-types: text/html,text/xml,text/plain,text/css, application/javascript, application/json + min-response-size: 1024 + +# =================================================================== +# JHipster specific properties +# +# Full reference is available at: https://jhipster.github.io/common-application-properties/ +# =================================================================== + +jhipster: + http: + version: V_1_1 # To use HTTP/2 you will need SSL support (see above the "server.ssl" configuration) + cache: # Used by the CachingHttpHeadersFilter + timeToLiveInDays: 1461 + cache: # Cache configuration + ehcache: # Ehcache configuration + time-to-live-seconds: 3600 # By default objects stay 1 hour in the cache + max-entries: 1000 # Number of objects in each cache entry + security: + authentication: + jwt: + secret: e1d4b69d3f953e3fa622121e882e6f459ca20ca4 + # Token is valid 24 hours + token-validity-in-seconds: 86400 + token-validity-in-seconds-for-remember-me: 2592000 + mail: # specific JHipster mail property, for standard properties see MailProperties + from: baeldung@localhost + base-url: http://my-server-url-to-change # Modify according to your server's URL + metrics: # DropWizard Metrics configuration, used by MetricsConfiguration + jmx.enabled: true + graphite: + enabled: false + host: localhost + port: 2003 + prefix: baeldung + prometheus: + enabled: false + endpoint: /prometheusMetrics + logs: # Reports Dropwizard metrics in the logs + enabled: false + reportFrequency: 60 # in seconds + logging: + logstash: # Forward logs to logstash over a socket, used by LoggingConfiguration + enabled: false + host: localhost + port: 5000 + queue-size: 512 + +# =================================================================== +# Application specific properties +# Add your own application properties here, see the ApplicationProperties class +# to have type-safe configuration, like in the JHipsterProperties above +# +# More documentation is available at: +# https://jhipster.github.io/common-application-properties/ +# =================================================================== + +application: diff --git a/jhipster/src/main/resources/config/application.yml b/jhipster/src/main/resources/config/application.yml new file mode 100644 index 000000000000..ed48baf853dd --- /dev/null +++ b/jhipster/src/main/resources/config/application.yml @@ -0,0 +1,106 @@ +# =================================================================== +# Spring Boot configuration. +# +# This configuration will be overriden by the Spring profile you use, +# for example application-dev.yml if you use the "dev" profile. +# +# More information on profiles: https://jhipster.github.io/profiles/ +# More information on configuration properties: https://jhipster.github.io/common-application-properties/ +# =================================================================== + +# =================================================================== +# Standard Spring Boot properties. +# Full reference is available at: +# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html +# =================================================================== + +management: + security: + roles: ADMIN + context-path: /management + health: + mail: + enabled: false # When using the MailService, configure an SMTP server and set this to true +spring: + application: + name: baeldung + profiles: + # The commented value for `active` can be replaced with valid Spring profiles to load. + # Otherwise, it will be filled in by maven when building the WAR file + # Either way, it can be overridden by `--spring.profiles.active` value passed in the commandline or `-Dspring.profiles.active` set in `JAVA_OPTS` + active: #spring.profiles.active# + jackson: + serialization.write_dates_as_timestamps: false + jpa: + open-in-view: false + hibernate: + ddl-auto: none + naming: + physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy + implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy + messages: + basename: i18n/messages + mvc: + favicon: + enabled: false + thymeleaf: + mode: XHTML + +security: + basic: + enabled: false + +server: + session: + cookie: + http-only: true + +info: + project: + version: #project.version# + +# =================================================================== +# JHipster specific properties +# +# Full reference is available at: https://jhipster.github.io/common-application-properties/ +# =================================================================== + +jhipster: + async: + core-pool-size: 2 + max-pool-size: 50 + queue-capacity: 10000 + # By default CORS is disabled. Uncomment to enable. + #cors: + #allowed-origins: "*" + #allowed-methods: GET, PUT, POST, DELETE, OPTIONS + #allowed-headers: "*" + #exposed-headers: + #allow-credentials: true + #max-age: 1800 + mail: + from: baeldung@localhost + swagger: + default-include-pattern: /api/.* + title: baeldung API + description: baeldung API documentation + version: 0.0.1 + terms-of-service-url: + contact-name: + contact-url: + contact-email: + license: + license-url: + ribbon: + display-on-active-profiles: dev + +# =================================================================== +# Application specific properties +# Add your own application properties here, see the ApplicationProperties class +# to have type-safe configuration, like in the JHipsterProperties above +# +# More documentation is available at: +# https://jhipster.github.io/common-application-properties/ +# =================================================================== + +application: diff --git a/jhipster/src/main/resources/config/liquibase/authorities.csv b/jhipster/src/main/resources/config/liquibase/authorities.csv new file mode 100644 index 000000000000..af5c6dfa186d --- /dev/null +++ b/jhipster/src/main/resources/config/liquibase/authorities.csv @@ -0,0 +1,3 @@ +name +ROLE_ADMIN +ROLE_USER diff --git a/jhipster/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml b/jhipster/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml new file mode 100644 index 000000000000..98ac548808ef --- /dev/null +++ b/jhipster/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jhipster/src/main/resources/config/liquibase/changelog/20170316223211_added_entity_Post.xml b/jhipster/src/main/resources/config/liquibase/changelog/20170316223211_added_entity_Post.xml new file mode 100644 index 000000000000..ce4773262c6e --- /dev/null +++ b/jhipster/src/main/resources/config/liquibase/changelog/20170316223211_added_entity_Post.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jhipster/src/main/resources/config/liquibase/changelog/20170316223211_added_entity_constraints_Post.xml b/jhipster/src/main/resources/config/liquibase/changelog/20170316223211_added_entity_constraints_Post.xml new file mode 100644 index 000000000000..204098037184 --- /dev/null +++ b/jhipster/src/main/resources/config/liquibase/changelog/20170316223211_added_entity_constraints_Post.xml @@ -0,0 +1,18 @@ + + + + + + + + + diff --git a/jhipster/src/main/resources/config/liquibase/changelog/20170316224021_added_entity_Comment.xml b/jhipster/src/main/resources/config/liquibase/changelog/20170316224021_added_entity_Comment.xml new file mode 100644 index 000000000000..d0b26503e113 --- /dev/null +++ b/jhipster/src/main/resources/config/liquibase/changelog/20170316224021_added_entity_Comment.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jhipster/src/main/resources/config/liquibase/changelog/20170316224021_added_entity_constraints_Comment.xml b/jhipster/src/main/resources/config/liquibase/changelog/20170316224021_added_entity_constraints_Comment.xml new file mode 100644 index 000000000000..b7670e747cc2 --- /dev/null +++ b/jhipster/src/main/resources/config/liquibase/changelog/20170316224021_added_entity_constraints_Comment.xml @@ -0,0 +1,18 @@ + + + + + + + + + diff --git a/jhipster/src/main/resources/config/liquibase/master.xml b/jhipster/src/main/resources/config/liquibase/master.xml new file mode 100644 index 000000000000..32eb4799897d --- /dev/null +++ b/jhipster/src/main/resources/config/liquibase/master.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/jhipster/src/main/resources/config/liquibase/users.csv b/jhipster/src/main/resources/config/liquibase/users.csv new file mode 100644 index 000000000000..b25922b69906 --- /dev/null +++ b/jhipster/src/main/resources/config/liquibase/users.csv @@ -0,0 +1,5 @@ +id;login;password_hash;first_name;last_name;email;image_url;activated;lang_key;created_by;last_modified_by +1;system;$2a$10$mE.qmcV0mFU5NcKh73TZx.z4ueI/.bDWbj0T1BYyqP481kGGarKLG;System;System;system@localhost;;true;en;system;system +2;anonymoususer;$2a$10$j8S5d7Sr7.8VTOYNviDPOeWX8KcYILUVJBsYV83Y5NtECayypx9lO;Anonymous;User;anonymous@localhost;;true;en;system;system +3;admin;$2a$10$gSAhZrxMllrbgj/kkK9UceBPpChGWJA7SYIb1Mqo.n5aNLq1/oRrC;Administrator;Administrator;admin@localhost;;true;en;system;system +4;user;$2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K;User;User;user@localhost;;true;en;system;system diff --git a/jhipster/src/main/resources/config/liquibase/users_authorities.csv b/jhipster/src/main/resources/config/liquibase/users_authorities.csv new file mode 100644 index 000000000000..06c5feeeeace --- /dev/null +++ b/jhipster/src/main/resources/config/liquibase/users_authorities.csv @@ -0,0 +1,6 @@ +user_id;authority_name +1;ROLE_ADMIN +1;ROLE_USER +3;ROLE_ADMIN +3;ROLE_USER +4;ROLE_USER diff --git a/jhipster/src/main/resources/i18n/messages.properties b/jhipster/src/main/resources/i18n/messages.properties new file mode 100644 index 000000000000..1c28002acd9d --- /dev/null +++ b/jhipster/src/main/resources/i18n/messages.properties @@ -0,0 +1,22 @@ +# Error page +error.title=Your request cannot be processed +error.subtitle=Sorry, an error has occurred. +error.status=Status: +error.message=Message: + +# Activation e-mail +email.activation.title=baeldung account activation +email.activation.greeting=Dear {0} +email.activation.text1=Your baeldung account has been created, please click on the URL below to activate it: +email.activation.text2=Regards, +email.signature=baeldung Team. + +# Creation email +email.creation.text1=Your baeldung account has been created, please click on the URL below to access it: + +# Reset e-mail +email.reset.title=baeldung password reset +email.reset.greeting=Dear {0} +email.reset.text1=For your baeldung account a password reset was requested, please click on the URL below to reset it: +email.reset.text2=Regards, + diff --git a/jhipster/src/main/resources/i18n/messages_en.properties b/jhipster/src/main/resources/i18n/messages_en.properties new file mode 100644 index 000000000000..1c28002acd9d --- /dev/null +++ b/jhipster/src/main/resources/i18n/messages_en.properties @@ -0,0 +1,22 @@ +# Error page +error.title=Your request cannot be processed +error.subtitle=Sorry, an error has occurred. +error.status=Status: +error.message=Message: + +# Activation e-mail +email.activation.title=baeldung account activation +email.activation.greeting=Dear {0} +email.activation.text1=Your baeldung account has been created, please click on the URL below to activate it: +email.activation.text2=Regards, +email.signature=baeldung Team. + +# Creation email +email.creation.text1=Your baeldung account has been created, please click on the URL below to access it: + +# Reset e-mail +email.reset.title=baeldung password reset +email.reset.greeting=Dear {0} +email.reset.text1=For your baeldung account a password reset was requested, please click on the URL below to reset it: +email.reset.text2=Regards, + diff --git a/jhipster/src/main/resources/logback-spring.xml b/jhipster/src/main/resources/logback-spring.xml new file mode 100644 index 000000000000..3c62a70c310e --- /dev/null +++ b/jhipster/src/main/resources/logback-spring.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + diff --git a/jhipster/src/main/resources/mails/activationEmail.html b/jhipster/src/main/resources/mails/activationEmail.html new file mode 100644 index 000000000000..9fb22a779685 --- /dev/null +++ b/jhipster/src/main/resources/mails/activationEmail.html @@ -0,0 +1,24 @@ + + + + JHipster activation + + + +

+ Dear +

+

+ Your JHipster account has been created, please click on the URL below to activate it: +

+

+ Activation Link +

+

+ Regards, +
+ JHipster. +

+ + diff --git a/jhipster/src/main/resources/mails/creationEmail.html b/jhipster/src/main/resources/mails/creationEmail.html new file mode 100644 index 000000000000..a59d91da0c4b --- /dev/null +++ b/jhipster/src/main/resources/mails/creationEmail.html @@ -0,0 +1,24 @@ + + + + JHipster creation + + + +

+ Dear +

+

+ Your JHipster account has been created, please click on the URL below to access it: +

+

+ login +

+

+ Regards, +
+ JHipster. +

+ + diff --git a/jhipster/src/main/resources/mails/passwordResetEmail.html b/jhipster/src/main/resources/mails/passwordResetEmail.html new file mode 100644 index 000000000000..30d5f62c60cb --- /dev/null +++ b/jhipster/src/main/resources/mails/passwordResetEmail.html @@ -0,0 +1,24 @@ + + + + JHipster password reset + + + +

+ Dear +

+

+ For your JHipster account a password reset was requested, please click on the URL below to reset it: +

+

+ Reset Link +

+

+ Regards, +
+ JHipster. +

+ + diff --git a/jhipster/src/main/resources/templates/error.html b/jhipster/src/main/resources/templates/error.html new file mode 100644 index 000000000000..774b080d6c43 --- /dev/null +++ b/jhipster/src/main/resources/templates/error.html @@ -0,0 +1,162 @@ + + + + + Your request cannot be processed + + + +
+

Your request cannot be processed :(

+ +

Sorry, an error has occurred.

+ + Status:  ()
+ + Message: 
+
+ + + +
+ + diff --git a/jhipster/src/main/webapp/404.html b/jhipster/src/main/webapp/404.html new file mode 100644 index 000000000000..8d7925a892c6 --- /dev/null +++ b/jhipster/src/main/webapp/404.html @@ -0,0 +1,60 @@ + + + + + Page Not Found + + + + +

Page Not Found

+

Sorry, but the page you were trying to view does not exist.

+ + + diff --git a/jhipster/src/main/webapp/app/account/account.module.ts b/jhipster/src/main/webapp/app/account/account.module.ts new file mode 100644 index 000000000000..09b19a7555c6 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/account.module.ts @@ -0,0 +1,45 @@ +import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +import { BaeldungSharedModule } from '../shared'; + +import { + Register, + Activate, + Password, + PasswordResetInit, + PasswordResetFinish, + PasswordStrengthBarComponent, + RegisterComponent, + ActivateComponent, + PasswordComponent, + PasswordResetInitComponent, + PasswordResetFinishComponent, + SettingsComponent, + accountState +} from './'; + +@NgModule({ + imports: [ + BaeldungSharedModule, + RouterModule.forRoot(accountState, { useHash: true }) + ], + declarations: [ + ActivateComponent, + RegisterComponent, + PasswordComponent, + PasswordStrengthBarComponent, + PasswordResetInitComponent, + PasswordResetFinishComponent, + SettingsComponent + ], + providers: [ + Register, + Activate, + Password, + PasswordResetInit, + PasswordResetFinish + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] +}) +export class BaeldungAccountModule {} diff --git a/jhipster/src/main/webapp/app/account/account.route.ts b/jhipster/src/main/webapp/app/account/account.route.ts new file mode 100644 index 000000000000..4715216cf3d4 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/account.route.ts @@ -0,0 +1,26 @@ +import { Routes, CanActivate } from '@angular/router'; + +import { UserRouteAccessService } from '../shared'; + +import { + activateRoute, + passwordRoute, + passwordResetFinishRoute, + passwordResetInitRoute, + registerRoute, + settingsRoute +} from './'; + +let ACCOUNT_ROUTES = [ + activateRoute, + passwordRoute, + passwordResetFinishRoute, + passwordResetInitRoute, + registerRoute, + settingsRoute +]; + +export const accountState: Routes = [{ + path: '', + children: ACCOUNT_ROUTES +}]; diff --git a/jhipster/src/main/webapp/app/account/activate/activate.component.html b/jhipster/src/main/webapp/app/account/activate/activate.component.html new file mode 100644 index 000000000000..25e3f2341797 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/activate/activate.component.html @@ -0,0 +1,19 @@ +
+
+
+

Activation

+ +
+ + Your user has been activated. Please + sign in. + +
+ +
+ Your user could not be activated. Please use the registration form to sign up. +
+ +
+
+
diff --git a/jhipster/src/main/webapp/app/account/activate/activate.component.ts b/jhipster/src/main/webapp/app/account/activate/activate.component.ts new file mode 100644 index 000000000000..dbaaa7d676f6 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/activate/activate.component.ts @@ -0,0 +1,42 @@ +import { Component, OnInit } from '@angular/core'; +import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { ActivatedRoute } from '@angular/router'; +import { JhiLanguageService } from 'ng-jhipster'; + +import { Activate } from './activate.service'; +import { LoginModalService } from '../../shared'; + +@Component({ + selector: 'jhi-activate', + templateUrl: './activate.component.html' +}) +export class ActivateComponent implements OnInit { + error: string; + success: string; + modalRef: NgbModalRef; + + constructor( + private jhiLanguageService: JhiLanguageService, + private activate: Activate, + private loginModalService: LoginModalService, + private route: ActivatedRoute + ) { + this.jhiLanguageService.setLocations(['activate']); + } + + ngOnInit () { + this.route.queryParams.subscribe(params => { + this.activate.get(params['key']).subscribe(() => { + this.error = null; + this.success = 'OK'; + }, () => { + this.success = null; + this.error = 'ERROR'; + }); + }); + } + + login() { + this.modalRef = this.loginModalService.open(); + } +} diff --git a/jhipster/src/main/webapp/app/account/activate/activate.route.ts b/jhipster/src/main/webapp/app/account/activate/activate.route.ts new file mode 100644 index 000000000000..8f1bb32b5850 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/activate/activate.route.ts @@ -0,0 +1,14 @@ +import { Route } from '@angular/router'; + +import { UserRouteAccessService } from '../../shared'; +import { ActivateComponent } from './activate.component'; + +export const activateRoute: Route = { + path: 'activate', + component: ActivateComponent, + data: { + authorities: [], + pageTitle: 'activate.title' + }, + canActivate: [UserRouteAccessService] +}; diff --git a/jhipster/src/main/webapp/app/account/activate/activate.service.ts b/jhipster/src/main/webapp/app/account/activate/activate.service.ts new file mode 100644 index 000000000000..f877a4d50e03 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/activate/activate.service.ts @@ -0,0 +1,18 @@ +import { Injectable } from '@angular/core'; +import { Http, Response, URLSearchParams } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +@Injectable() +export class Activate { + + constructor (private http: Http) {} + + get(key: string): Observable { + let params: URLSearchParams = new URLSearchParams(); + params.set('key', key); + + return this.http.get('api/activate', { + search: params + }).map((res: Response) => res); + } +} diff --git a/jhipster/src/main/webapp/app/account/index.ts b/jhipster/src/main/webapp/app/account/index.ts new file mode 100644 index 000000000000..aeada0551ce1 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/index.ts @@ -0,0 +1,19 @@ +export * from './activate/activate.component'; +export * from './activate/activate.service'; +export * from './activate/activate.route'; +export * from './password/password.component'; +export * from './password/password-strength-bar.component'; +export * from './password/password.service'; +export * from './password/password.route'; +export * from './password-reset/finish/password-reset-finish.component'; +export * from './password-reset/finish/password-reset-finish.service'; +export * from './password-reset/finish/password-reset-finish.route'; +export * from './password-reset/init/password-reset-init.component'; +export * from './password-reset/init/password-reset-init.service'; +export * from './password-reset/init/password-reset-init.route'; +export * from './register/register.component'; +export * from './register/register.service'; +export * from './register/register.route'; +export * from './settings/settings.component'; +export * from './settings/settings.route'; +export * from './account.route'; diff --git a/jhipster/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.html b/jhipster/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.html new file mode 100644 index 000000000000..a1dfde055ca7 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.html @@ -0,0 +1,77 @@ +
+
+
+

Reset password

+ +
+ The password reset key is missing. +
+ +
+

Choose a new password

+
+ +
+

Your password couldn't be reset. Remember a password request is only valid for 24 hours.

+
+ +

+ Your password has been reset. Please + sign in. +

+ +
+ The password and its confirmation do not match! +
+ +
+
+
+ + +
+ + Your password is required. + + + Your password is required to be at least 4 characters. + + + Your password cannot be longer than 50 characters. + +
+ +
+ +
+ + +
+ + Your password confirmation is required. + + + Your password confirmation is required to be at least 4 characters. + + + Your password confirmation cannot be longer than 50 characters. + +
+
+ +
+
+ +
+
+
diff --git a/jhipster/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts b/jhipster/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts new file mode 100644 index 000000000000..f1889920bd55 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts @@ -0,0 +1,65 @@ +import { Component, OnInit, AfterViewInit, Renderer, ElementRef } from '@angular/core'; +import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { ActivatedRoute } from '@angular/router'; +import { JhiLanguageService } from 'ng-jhipster'; + +import { PasswordResetFinish } from './password-reset-finish.service'; +import { LoginModalService } from '../../../shared'; + +@Component({ + selector: 'jhi-password-reset-finish', + templateUrl: './password-reset-finish.component.html' +}) +export class PasswordResetFinishComponent implements OnInit, AfterViewInit { + confirmPassword: string; + doNotMatch: string; + error: string; + keyMissing: boolean; + resetAccount: any; + success: string; + modalRef: NgbModalRef; + key: string; + + constructor( + private jhiLanguageService: JhiLanguageService, + private passwordResetFinish: PasswordResetFinish, + private loginModalService: LoginModalService, + private route: ActivatedRoute, + private elementRef: ElementRef, private renderer: Renderer + ) { + this.jhiLanguageService.setLocations(['reset']); + } + + ngOnInit() { + this.route.queryParams.subscribe(params => { + this.key = params['key']; + }); + this.resetAccount = {}; + this.keyMissing = !this.key; + } + + ngAfterViewInit() { + if (this.elementRef.nativeElement.querySelector('#password') != null) { + this.renderer.invokeElementMethod(this.elementRef.nativeElement.querySelector('#password'), 'focus', []); + } + } + + finishReset() { + this.doNotMatch = null; + this.error = null; + if (this.resetAccount.password !== this.confirmPassword) { + this.doNotMatch = 'ERROR'; + } else { + this.passwordResetFinish.save({key: this.key, newPassword: this.resetAccount.password}).subscribe(() => { + this.success = 'OK'; + }, () => { + this.success = null; + this.error = 'ERROR'; + }); + } + } + + login() { + this.modalRef = this.loginModalService.open(); + } +} diff --git a/jhipster/src/main/webapp/app/account/password-reset/finish/password-reset-finish.route.ts b/jhipster/src/main/webapp/app/account/password-reset/finish/password-reset-finish.route.ts new file mode 100644 index 000000000000..7b02653ed567 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/password-reset/finish/password-reset-finish.route.ts @@ -0,0 +1,14 @@ +import { Route } from '@angular/router'; + +import { UserRouteAccessService } from '../../../shared'; +import { PasswordResetFinishComponent } from './password-reset-finish.component'; + +export const passwordResetFinishRoute: Route = { + path: 'reset/finish', + component: PasswordResetFinishComponent, + data: { + authorities: [], + pageTitle: 'global.menu.account.password' + }, + canActivate: [UserRouteAccessService] +}; diff --git a/jhipster/src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.ts b/jhipster/src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.ts new file mode 100644 index 000000000000..abd81374b065 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.ts @@ -0,0 +1,13 @@ +import { Injectable } from '@angular/core'; +import { Http } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +@Injectable() +export class PasswordResetFinish { + + constructor (private http: Http) {} + + save(keyAndPassword: any): Observable { + return this.http.post('api/account/reset_password/finish', keyAndPassword); + } +} diff --git a/jhipster/src/main/webapp/app/account/password-reset/init/password-reset-init.component.html b/jhipster/src/main/webapp/app/account/password-reset/init/password-reset-init.component.html new file mode 100644 index 000000000000..2503433275e0 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/password-reset/init/password-reset-init.component.html @@ -0,0 +1,47 @@ +
+
+
+

Reset your password

+ +
+ E-Mail address isn't registered! Please check and try again. +
+ +
+

Enter the e-mail address you used to register.

+
+ +
+

Check your e-mails for details on how to reset your password.

+
+ +
+
+ + +
+ + Your e-mail is required. + + + Your e-mail is invalid. + + + Your e-mail is required to be at least 5 characters. + + + Your e-mail cannot be longer than 100 characters. + +
+
+ +
+ +
+
+
diff --git a/jhipster/src/main/webapp/app/account/password-reset/init/password-reset-init.component.ts b/jhipster/src/main/webapp/app/account/password-reset/init/password-reset-init.component.ts new file mode 100644 index 000000000000..eba521dc050b --- /dev/null +++ b/jhipster/src/main/webapp/app/account/password-reset/init/password-reset-init.component.ts @@ -0,0 +1,49 @@ +import { Component, OnInit, AfterViewInit, Renderer, ElementRef } from '@angular/core'; +import { JhiLanguageService } from 'ng-jhipster'; + +import { PasswordResetInit } from './password-reset-init.service'; + +@Component({ + selector: 'jhi-password-reset-init', + templateUrl: './password-reset-init.component.html' +}) +export class PasswordResetInitComponent implements OnInit, AfterViewInit { + error: string; + errorEmailNotExists: string; + resetAccount: any; + success: string; + + constructor( + private jhiLanguageService: JhiLanguageService, + private passwordResetInit: PasswordResetInit, + private elementRef: ElementRef, + private renderer: Renderer + ) { + this.jhiLanguageService.setLocations(['reset']); + } + + ngOnInit() { + this.resetAccount = {}; + } + + ngAfterViewInit() { + this.renderer.invokeElementMethod(this.elementRef.nativeElement.querySelector('#email'), 'focus', []); + } + + requestReset () { + + this.error = null; + this.errorEmailNotExists = null; + + this.passwordResetInit.save(this.resetAccount.email).subscribe(() => { + this.success = 'OK'; + }, (response) => { + this.success = null; + if (response.status === 400 && response.data === 'e-mail address not registered') { + this.errorEmailNotExists = 'ERROR'; + } else { + this.error = 'ERROR'; + } + }); + } +} diff --git a/jhipster/src/main/webapp/app/account/password-reset/init/password-reset-init.route.ts b/jhipster/src/main/webapp/app/account/password-reset/init/password-reset-init.route.ts new file mode 100644 index 000000000000..37f7e8102dd7 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/password-reset/init/password-reset-init.route.ts @@ -0,0 +1,14 @@ +import { Route } from '@angular/router'; + +import { UserRouteAccessService } from '../../../shared'; +import { PasswordResetInitComponent } from './password-reset-init.component'; + +export const passwordResetInitRoute: Route = { + path: 'reset/request', + component: PasswordResetInitComponent, + data: { + authorities: [], + pageTitle: 'global.menu.account.password' + }, + canActivate: [UserRouteAccessService] +}; diff --git a/jhipster/src/main/webapp/app/account/password-reset/init/password-reset-init.service.ts b/jhipster/src/main/webapp/app/account/password-reset/init/password-reset-init.service.ts new file mode 100644 index 000000000000..fa466cd6cc38 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/password-reset/init/password-reset-init.service.ts @@ -0,0 +1,13 @@ +import { Injectable } from '@angular/core'; +import { Http } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +@Injectable() +export class PasswordResetInit { + + constructor (private http: Http) {} + + save(mail: string): Observable { + return this.http.post('api/account/reset_password/init', mail); + } +} diff --git a/jhipster/src/main/webapp/app/account/password/password-strength-bar.component.ts b/jhipster/src/main/webapp/app/account/password/password-strength-bar.component.ts new file mode 100644 index 000000000000..2a61d133d65c --- /dev/null +++ b/jhipster/src/main/webapp/app/account/password/password-strength-bar.component.ts @@ -0,0 +1,89 @@ +import { Component, ElementRef, Input, Renderer } from '@angular/core'; + +@Component({ + selector: 'jhi-password-strength-bar', + template: ` +
+ Password strength: +
    +
  • +
  • +
  • +
  • +
  • +
+
`, + styleUrls: [ + 'password-strength-bar.scss' + ] +}) +export class PasswordStrengthBarComponent { + + colors = ['#F00', '#F90', '#FF0', '#9F0', '#0F0']; + + constructor(private renderer: Renderer, private elementRef: ElementRef) { } + + measureStrength(p: string): number { + + let force = 0; + let regex = /[$-/:-?{-~!"^_`\[\]]/g; // " + + let lowerLetters = /[a-z]+/.test(p); + let upperLetters = /[A-Z]+/.test(p); + let numbers = /[0-9]+/.test(p); + let symbols = regex.test(p); + + let flags = [lowerLetters, upperLetters, numbers, symbols]; + let passedMatches = flags.filter( (isMatchedFlag: boolean) => { + return isMatchedFlag === true; + }).length; + + force += 2 * p.length + ((p.length >= 10) ? 1 : 0); + force += passedMatches * 10; + + // penality (short password) + force = (p.length <= 6) ? Math.min(force, 10) : force; + + // penality (poor variety of characters) + force = (passedMatches === 1) ? Math.min(force, 10) : force; + force = (passedMatches === 2) ? Math.min(force, 20) : force; + force = (passedMatches === 3) ? Math.min(force, 40) : force; + + return force; + }; + + getColor(s: number): any { + let idx = 0; + if (s <= 10) { + idx = 0; + } else if (s <= 20) { + idx = 1; + } else if (s <= 30) { + idx = 2; + } else if (s <= 40) { + idx = 3; + } else { + idx = 4; + } + return {idx: idx + 1, col: this.colors[idx]}; + }; + + @Input() + set passwordToCheck(password: string) { + if (password) { + let c = this.getColor(this.measureStrength(password)); + let element = this.elementRef.nativeElement; + if ( element.className ) { + this.renderer.setElementClass(element, element.className , false); + } + let lis = element.getElementsByTagName('li'); + for (let i = 0; i < lis.length; i++) { + if (i < c.idx) { + this.renderer.setElementStyle(lis[i], 'backgroundColor', c.col); + } else { + this.renderer.setElementStyle(lis[i], 'backgroundColor', '#DDD'); + } + } + } + } +} diff --git a/jhipster/src/main/webapp/app/account/password/password-strength-bar.scss b/jhipster/src/main/webapp/app/account/password/password-strength-bar.scss new file mode 100644 index 000000000000..8f8effcb60d7 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/password/password-strength-bar.scss @@ -0,0 +1,23 @@ +/* ========================================================================== +start Password strength bar style +========================================================================== */ +ul#strength { + display:inline; + list-style:none; + margin:0; + margin-left:15px; + padding:0; + vertical-align:2px; +} + +.point { + background:#DDD; + border-radius:2px; + display:inline-block; + height:5px; + margin-right:1px; + width:20px; + &:last { + margin:0 !important; + } +} diff --git a/jhipster/src/main/webapp/app/account/password/password.component.html b/jhipster/src/main/webapp/app/account/password/password.component.html new file mode 100644 index 000000000000..2534bccdfca9 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/password/password.component.html @@ -0,0 +1,65 @@ +
+
+
+

Password for [{{account.login}}]

+ +
+ Password changed! +
+
+ An error has occurred! The password could not be changed. +
+ +
+ The password and its confirmation do not match! +
+ +
+ +
+ + +
+ + Your password is required. + + + Your password is required to be at least 4 characters. + + + Your password cannot be longer than 50 characters. + +
+ +
+
+ + +
+ + Your confirmation password is required. + + + Your confirmation password is required to be at least 4 characters. + + + Your confirmation password cannot be longer than 50 characters. + +
+
+ + +
+
+
+
diff --git a/jhipster/src/main/webapp/app/account/password/password.component.ts b/jhipster/src/main/webapp/app/account/password/password.component.ts new file mode 100644 index 000000000000..f34eae423cf3 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/password/password.component.ts @@ -0,0 +1,49 @@ +import { Component, OnInit } from '@angular/core'; +import { JhiLanguageService } from 'ng-jhipster'; + +import { Principal } from '../../shared'; +import { Password } from './password.service'; + +@Component({ + selector: 'jhi-password', + templateUrl: './password.component.html' +}) +export class PasswordComponent implements OnInit { + doNotMatch: string; + error: string; + success: string; + account: any; + password: string; + confirmPassword: string; + + constructor( + private jhiLanguageService: JhiLanguageService, + private passwordService: Password, + private principal: Principal + ) { + this.jhiLanguageService.setLocations(['password']); + } + + ngOnInit () { + this.principal.identity().then((account) => { + this.account = account; + }); + } + + changePassword () { + if (this.password !== this.confirmPassword) { + this.error = null; + this.success = null; + this.doNotMatch = 'ERROR'; + } else { + this.doNotMatch = null; + this.passwordService.save(this.password).subscribe(() => { + this.error = null; + this.success = 'OK'; + }, () => { + this.success = null; + this.error = 'ERROR'; + }); + } + } +} diff --git a/jhipster/src/main/webapp/app/account/password/password.route.ts b/jhipster/src/main/webapp/app/account/password/password.route.ts new file mode 100644 index 000000000000..d5a71184584f --- /dev/null +++ b/jhipster/src/main/webapp/app/account/password/password.route.ts @@ -0,0 +1,14 @@ +import { Route } from '@angular/router'; + +import { UserRouteAccessService } from '../../shared'; +import { PasswordComponent } from './password.component'; + +export const passwordRoute: Route = { + path: 'password', + component: PasswordComponent, + data: { + authorities: ['ROLE_USER'], + pageTitle: 'global.menu.account.password' + }, + canActivate: [UserRouteAccessService] +}; diff --git a/jhipster/src/main/webapp/app/account/password/password.service.ts b/jhipster/src/main/webapp/app/account/password/password.service.ts new file mode 100644 index 000000000000..0c220d8816d2 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/password/password.service.ts @@ -0,0 +1,13 @@ +import { Injectable } from '@angular/core'; +import { Http } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +@Injectable() +export class Password { + + constructor (private http: Http) {} + + save(newPassword: string): Observable { + return this.http.post('api/account/change_password', newPassword); + } +} diff --git a/jhipster/src/main/webapp/app/account/register/register.component.html b/jhipster/src/main/webapp/app/account/register/register.component.html new file mode 100644 index 000000000000..14a0c851e252 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/register/register.component.html @@ -0,0 +1,122 @@ +
+
+
+

Registration

+ +
+ Registration saved! Please check your email for confirmation. +
+ +
+ Registration failed! Please try again later. +
+ +
+ Login name already registered! Please choose another one. +
+ +
+ E-mail is already in use! Please choose another one. +
+ +
+ The password and its confirmation do not match! +
+
+
+
+
+ + +
+ + Your username is required. + + + Your username is required to be at least 1 character. + + + Your username cannot be longer than 50 characters. + + + Your username can only contain lower-case letters and digits. + +
+
+
+ + +
+ + Your e-mail is required. + + + Your e-mail is invalid. + + + Your e-mail is required to be at least 5 characters. + + + Your e-mail cannot be longer than 100 characters. + +
+
+
+ + +
+ + Your password is required. + + + Your password is required to be at least 4 characters. + + + Your password cannot be longer than 50 characters. + +
+ +
+
+ + +
+ + Your confirmation password is required. + + + Your confirmation password is required to be at least 4 characters. + + + Your confirmation password cannot be longer than 50 characters. + +
+
+ + +
+

+
+ If you want to + sign in, you can try the default accounts:
- Administrator (login="admin" and password="admin")
- User (login="user" and password="user").
+
+
+
+
diff --git a/jhipster/src/main/webapp/app/account/register/register.component.ts b/jhipster/src/main/webapp/app/account/register/register.component.ts new file mode 100644 index 000000000000..dc5c4ef2f4f8 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/register/register.component.ts @@ -0,0 +1,73 @@ +import { Component, OnInit, AfterViewInit, Renderer, ElementRef } from '@angular/core'; +import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { JhiLanguageService } from 'ng-jhipster'; + +import { Register } from './register.service'; +import { LoginModalService } from '../../shared'; + +@Component({ + selector: 'jhi-register', + templateUrl: './register.component.html' +}) +export class RegisterComponent implements OnInit, AfterViewInit { + + confirmPassword: string; + doNotMatch: string; + error: string; + errorEmailExists: string; + errorUserExists: string; + registerAccount: any; + success: boolean; + modalRef: NgbModalRef; + + constructor( + private languageService: JhiLanguageService, + private loginModalService: LoginModalService, + private registerService: Register, + private elementRef: ElementRef, + private renderer: Renderer + ) { + this.languageService.setLocations(['register']); + } + + ngOnInit() { + this.success = false; + this.registerAccount = {}; + } + + ngAfterViewInit() { + this.renderer.invokeElementMethod(this.elementRef.nativeElement.querySelector('#login'), 'focus', []); + } + + register() { + if (this.registerAccount.password !== this.confirmPassword) { + this.doNotMatch = 'ERROR'; + } else { + this.doNotMatch = null; + this.error = null; + this.errorUserExists = null; + this.errorEmailExists = null; + this.languageService.getCurrent().then(key => { + this.registerAccount.langKey = key; + this.registerService.save(this.registerAccount).subscribe(() => { + this.success = true; + }, (response) => this.processError(response)); + }); + } + } + + openLogin() { + this.modalRef = this.loginModalService.open(); + } + + private processError(response) { + this.success = null; + if (response.status === 400 && response._body === 'login already in use') { + this.errorUserExists = 'ERROR'; + } else if (response.status === 400 && response._body === 'e-mail address already in use') { + this.errorEmailExists = 'ERROR'; + } else { + this.error = 'ERROR'; + } + } +} diff --git a/jhipster/src/main/webapp/app/account/register/register.route.ts b/jhipster/src/main/webapp/app/account/register/register.route.ts new file mode 100644 index 000000000000..3dc47f1e5896 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/register/register.route.ts @@ -0,0 +1,14 @@ +import { Route } from '@angular/router'; + +import { UserRouteAccessService } from '../../shared'; +import { RegisterComponent } from './register.component'; + +export const registerRoute: Route = { + path: 'register', + component: RegisterComponent, + data: { + authorities: [], + pageTitle: 'register.title' + }, + canActivate: [UserRouteAccessService] +}; diff --git a/jhipster/src/main/webapp/app/account/register/register.service.ts b/jhipster/src/main/webapp/app/account/register/register.service.ts new file mode 100644 index 000000000000..87bbe9d37b9e --- /dev/null +++ b/jhipster/src/main/webapp/app/account/register/register.service.ts @@ -0,0 +1,13 @@ +import { Injectable } from '@angular/core'; +import { Http } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +@Injectable() +export class Register { + + constructor (private http: Http) {} + + save(account: any): Observable { + return this.http.post('api/register', account); + } +} diff --git a/jhipster/src/main/webapp/app/account/settings/settings.component.html b/jhipster/src/main/webapp/app/account/settings/settings.component.html new file mode 100644 index 000000000000..82fca9e80479 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/settings/settings.component.html @@ -0,0 +1,86 @@ +
+
+
+

User settings for [{{settingsAccount.login}}]

+ +
+ Settings saved! +
+ + + +
+ +
+ + +
+ + Your first name is required. + + + Your first name is required to be at least 1 character. + + + Your first name cannot be longer than 50 characters. + +
+
+
+ + +
+ + Your last name is required. + + + Your last name is required to be at least 1 character. + + + Your last name cannot be longer than 50 characters. + +
+
+
+ + +
+ + Your e-mail is required. + + + Your e-mail is invalid. + + + Your e-mail is required to be at least 5 characters. + + + Your e-mail cannot be longer than 100 characters. + +
+
+
+ + +
+ +
+
+
+ +
diff --git a/jhipster/src/main/webapp/app/account/settings/settings.component.ts b/jhipster/src/main/webapp/app/account/settings/settings.component.ts new file mode 100644 index 000000000000..37f4c70e3b9d --- /dev/null +++ b/jhipster/src/main/webapp/app/account/settings/settings.component.ts @@ -0,0 +1,63 @@ +import { Component, OnInit } from '@angular/core'; +import { JhiLanguageService } from 'ng-jhipster'; + +import { Principal, AccountService, JhiLanguageHelper } from '../../shared'; + +@Component({ + selector: 'jhi-settings', + templateUrl: './settings.component.html' +}) +export class SettingsComponent implements OnInit { + error: string; + success: string; + settingsAccount: any; + languages: any[]; + + constructor( + private account: AccountService, + private principal: Principal, + private languageService: JhiLanguageService, + private languageHelper: JhiLanguageHelper + ) { + this.languageService.setLocations(['settings']); + } + + ngOnInit () { + this.principal.identity().then((account) => { + this.settingsAccount = this.copyAccount(account); + }); + this.languageHelper.getAll().then((languages) => { + this.languages = languages; + }); + } + + save () { + this.account.save(this.settingsAccount).subscribe(() => { + this.error = null; + this.success = 'OK'; + this.principal.identity(true).then((account) => { + this.settingsAccount = this.copyAccount(account); + }); + this.languageService.getCurrent().then((current) => { + if (this.settingsAccount.langKey !== current) { + this.languageService.changeLanguage(this.settingsAccount.langKey); + } + }); + }, () => { + this.success = null; + this.error = 'ERROR'; + }); + } + + copyAccount (account) { + return { + activated: account.activated, + email: account.email, + firstName: account.firstName, + langKey: account.langKey, + lastName: account.lastName, + login: account.login, + imageUrl: account.imageUrl + }; + } +} diff --git a/jhipster/src/main/webapp/app/account/settings/settings.route.ts b/jhipster/src/main/webapp/app/account/settings/settings.route.ts new file mode 100644 index 000000000000..9c9a852c7bbd --- /dev/null +++ b/jhipster/src/main/webapp/app/account/settings/settings.route.ts @@ -0,0 +1,14 @@ +import { Route } from '@angular/router'; + +import { UserRouteAccessService } from '../../shared'; +import { SettingsComponent } from './settings.component'; + +export const settingsRoute: Route = { + path: 'settings', + component: SettingsComponent, + data: { + authorities: ['ROLE_USER'], + pageTitle: 'global.menu.account.settings' + }, + canActivate: [UserRouteAccessService] +}; diff --git a/jhipster/src/main/webapp/app/admin/admin.module.ts b/jhipster/src/main/webapp/app/admin/admin.module.ts new file mode 100644 index 000000000000..4ac749bc78ce --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/admin.module.ts @@ -0,0 +1,73 @@ +import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { RouterModule } from '@angular/router'; +import { ParseLinks } from 'ng-jhipster'; + +import { BaeldungSharedModule } from '../shared'; + +import { + adminState, + AuditsComponent, + UserMgmtComponent, + UserDialogComponent, + UserDeleteDialogComponent, + UserMgmtDetailComponent, + UserMgmtDialogComponent, + UserMgmtDeleteDialogComponent, + LogsComponent, + JhiMetricsMonitoringModalComponent, + JhiMetricsMonitoringComponent, + JhiHealthModalComponent, + JhiHealthCheckComponent, + JhiConfigurationComponent, + JhiDocsComponent, + AuditsService, + JhiConfigurationService, + JhiHealthService, + JhiMetricsService, + LogsService, + UserResolvePagingParams, + UserResolve, + UserModalService +} from './'; + + +@NgModule({ + imports: [ + BaeldungSharedModule, + RouterModule.forRoot(adminState, { useHash: true }) + ], + declarations: [ + AuditsComponent, + UserMgmtComponent, + UserDialogComponent, + UserDeleteDialogComponent, + UserMgmtDetailComponent, + UserMgmtDialogComponent, + UserMgmtDeleteDialogComponent, + LogsComponent, + JhiConfigurationComponent, + JhiHealthCheckComponent, + JhiHealthModalComponent, + JhiDocsComponent, + JhiMetricsMonitoringComponent, + JhiMetricsMonitoringModalComponent + ], + entryComponents: [ + UserMgmtDialogComponent, + UserMgmtDeleteDialogComponent, + JhiHealthModalComponent, + JhiMetricsMonitoringModalComponent, + ], + providers: [ + AuditsService, + JhiConfigurationService, + JhiHealthService, + JhiMetricsService, + LogsService, + UserResolvePagingParams, + UserResolve, + UserModalService + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] +}) +export class BaeldungAdminModule {} diff --git a/jhipster/src/main/webapp/app/admin/admin.route.ts b/jhipster/src/main/webapp/app/admin/admin.route.ts new file mode 100644 index 000000000000..8e64d2d5c0ae --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/admin.route.ts @@ -0,0 +1,36 @@ +import { Routes, CanActivate } from '@angular/router'; + +import { + auditsRoute, + configurationRoute, + docsRoute, + healthRoute, + logsRoute, + metricsRoute, + userMgmtRoute, + userDialogRoute +} from './'; + +import { UserRouteAccessService } from '../shared'; + +let ADMIN_ROUTES = [ + auditsRoute, + configurationRoute, + docsRoute, + healthRoute, + logsRoute, + ...userMgmtRoute, + metricsRoute +]; + + +export const adminState: Routes = [{ + path: '', + data: { + authorities: ['ROLE_ADMIN'] + }, + canActivate: [UserRouteAccessService], + children: ADMIN_ROUTES +}, + ...userDialogRoute +]; diff --git a/jhipster/src/main/webapp/app/admin/audits/audit-data.model.ts b/jhipster/src/main/webapp/app/admin/audits/audit-data.model.ts new file mode 100644 index 000000000000..55a5e9c819b3 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/audits/audit-data.model.ts @@ -0,0 +1,6 @@ +export class AuditData { + constructor( + public remoteAddress: string, + public sessionId: string + ) { } +} diff --git a/jhipster/src/main/webapp/app/admin/audits/audit.model.ts b/jhipster/src/main/webapp/app/admin/audits/audit.model.ts new file mode 100644 index 000000000000..9ca753795c88 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/audits/audit.model.ts @@ -0,0 +1,10 @@ +import { AuditData } from './audit-data.model'; + +export class Audit { + constructor( + public data: AuditData, + public principal: string, + public timestamp: string, + public type: string + ) { } +} diff --git a/jhipster/src/main/webapp/app/admin/audits/audits.component.html b/jhipster/src/main/webapp/app/admin/audits/audits.component.html new file mode 100644 index 000000000000..9fcbaf7ecdcd --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/audits/audits.component.html @@ -0,0 +1,45 @@ +
+

Audits

+ +
+
+

Filter by date

+

+ from + + to + +

+
+
+ +
+ + + + + + + + + + + + + + + +
DateUserStateExtra data
{{audit.timestamp| date:'medium'}}{{audit.principal}}{{audit.type}} + {{audit.data.message}} + Remote Address {{audit.data.remoteAddress}} +
+
+
+
+ +
+
+ +
+
+
diff --git a/jhipster/src/main/webapp/app/admin/audits/audits.component.ts b/jhipster/src/main/webapp/app/admin/audits/audits.component.ts new file mode 100644 index 000000000000..84e6b40fe808 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/audits/audits.component.ts @@ -0,0 +1,99 @@ +import { Component, OnInit, Inject } from '@angular/core'; +import { DatePipe } from '@angular/common'; +import { ParseLinks, JhiLanguageService} from 'ng-jhipster'; + +import { Audit } from './audit.model'; +import { AuditsService } from './audits.service'; +import { ITEMS_PER_PAGE } from '../../shared'; +import { PaginationConfig } from '../../blocks/config/uib-pagination.config'; + +@Component({ + selector: 'jhi-audit', + templateUrl: './audits.component.html' +}) +export class AuditsComponent implements OnInit { + audits: Audit[]; + fromDate: string; + itemsPerPage: any; + links: any; + page: number; + orderProp: string; + reverse: boolean; + toDate: string; + totalItems: number; + + constructor( + private jhiLanguageService: JhiLanguageService, + private auditsService: AuditsService, + private parseLinks: ParseLinks, + private paginationConfig: PaginationConfig, + private datePipe: DatePipe + ) { + this.jhiLanguageService.setLocations(['audits']); + this.itemsPerPage = ITEMS_PER_PAGE; + this.page = 1; + this.reverse = false; + this.orderProp = 'timestamp'; + } + + getAudits() { + return this.sortAudits(this.audits); + } + + loadPage(page: number) { + this.page = page; + this.onChangeDate(); + } + + ngOnInit() { + this.today(); + this.previousMonth(); + this.onChangeDate(); + } + + onChangeDate() { + this.auditsService.query({page: this.page - 1, size: this.itemsPerPage, + fromDate: this.fromDate, toDate: this.toDate}).subscribe(res => { + + this.audits = res.json(); + this.links = this.parseLinks.parse(res.headers.get('link')); + this.totalItems = + res.headers.get('X-Total-Count'); + }); + } + + previousMonth() { + let dateFormat = 'yyyy-MM-dd'; + let fromDate: Date = new Date(); + + if (fromDate.getMonth() === 0) { + fromDate = new Date(fromDate.getFullYear() - 1, 11, fromDate.getDate()); + } else { + fromDate = new Date(fromDate.getFullYear(), fromDate.getMonth() - 1, fromDate.getDate()); + } + + this.fromDate = this.datePipe.transform(fromDate, dateFormat); + } + + today() { + let dateFormat = 'yyyy-MM-dd'; + // Today + 1 day - needed if the current day must be included + let today: Date = new Date(); + + let date = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1); + this.toDate = this.datePipe.transform(date, dateFormat); + } + + private sortAudits(audits: Audit[]) { + audits = audits.slice(0).sort((a, b) => { + if (a[this.orderProp] < b[this.orderProp]) { + return -1; + } else if ([b[this.orderProp] < a[this.orderProp]]) { + return 1; + } else { + return 0; + } + }); + + return this.reverse ? audits.reverse() : audits; + } +} diff --git a/jhipster/src/main/webapp/app/admin/audits/audits.route.ts b/jhipster/src/main/webapp/app/admin/audits/audits.route.ts new file mode 100644 index 000000000000..f0e0fa7be99c --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/audits/audits.route.ts @@ -0,0 +1,12 @@ +import { Route } from '@angular/router'; + +import { UserRouteAccessService } from '../../shared'; +import { AuditsComponent } from './audits.component'; + +export const auditsRoute: Route = { + path: 'audits', + component: AuditsComponent, + data: { + pageTitle: 'audits.title' + } +}; diff --git a/jhipster/src/main/webapp/app/admin/audits/audits.service.ts b/jhipster/src/main/webapp/app/admin/audits/audits.service.ts new file mode 100644 index 000000000000..b373b8915cbe --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/audits/audits.service.ts @@ -0,0 +1,23 @@ +import { Injectable } from '@angular/core'; +import { Http, Response, URLSearchParams } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +@Injectable() +export class AuditsService { + constructor(private http: Http) { } + + query(req: any): Observable { + let params: URLSearchParams = new URLSearchParams(); + params.set('fromDate', req.fromDate); + params.set('toDate', req.toDate); + params.set('page', req.page); + params.set('size', req.size); + params.set('sort', req.sort); + + let options = { + search: params + }; + + return this.http.get('management/audits', options); + } +} diff --git a/jhipster/src/main/webapp/app/admin/configuration/configuration.component.html b/jhipster/src/main/webapp/app/admin/configuration/configuration.component.html new file mode 100644 index 000000000000..1d64b41c2955 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/configuration/configuration.component.html @@ -0,0 +1,46 @@ +
+

Configuration

+ + Filter (by prefix) + + + + + + + + + + + + + + +
PrefixProperties
{{entry.prefix}} +
+
{{key}}
+
+ {{entry.properties[key]}} +
+
+
+
+ + + + + + + + + + + + + + +
PropertyValue
{{item.key}} + {{item.val}} +
+
+
diff --git a/jhipster/src/main/webapp/app/admin/configuration/configuration.component.ts b/jhipster/src/main/webapp/app/admin/configuration/configuration.component.ts new file mode 100644 index 000000000000..88fbc32250e6 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/configuration/configuration.component.ts @@ -0,0 +1,48 @@ +import { Component, OnInit } from '@angular/core'; +import { JhiLanguageService } from 'ng-jhipster'; + +import { JhiConfigurationService } from './configuration.service'; + +@Component({ + selector: 'jhi-configuration', + templateUrl: './configuration.component.html' +}) +export class JhiConfigurationComponent implements OnInit { + allConfiguration: any = null; + configuration: any = null; + configKeys: any[]; + filter: string; + orderProp: string; + reverse: boolean; + + constructor( + private jhiLanguageService: JhiLanguageService, + private configurationService: JhiConfigurationService + ) { + this.jhiLanguageService.setLocations(['configuration']); + this.configKeys = []; + this.filter = ''; + this.orderProp = 'prefix'; + this.reverse = false; + } + + keys(dict): Array { + return (dict === undefined) ? [] : Object.keys(dict); + } + + ngOnInit() { + this.configurationService.get().subscribe((configuration) => { + this.configuration = configuration; + + for (let config of configuration) { + if (config.properties !== undefined) { + this.configKeys.push(Object.keys(config.properties)); + } + } + }); + + this.configurationService.getEnv().subscribe((configuration) => { + this.allConfiguration = configuration; + }); + } +} diff --git a/jhipster/src/main/webapp/app/admin/configuration/configuration.route.ts b/jhipster/src/main/webapp/app/admin/configuration/configuration.route.ts new file mode 100644 index 000000000000..06866237b039 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/configuration/configuration.route.ts @@ -0,0 +1,12 @@ +import { Route } from '@angular/router'; + +import { UserRouteAccessService } from '../../shared'; +import { JhiConfigurationComponent } from './configuration.component'; + +export const configurationRoute: Route = { + path: 'jhi-configuration', + component: JhiConfigurationComponent, + data: { + pageTitle: 'configuration.title' + } +}; diff --git a/jhipster/src/main/webapp/app/admin/configuration/configuration.service.ts b/jhipster/src/main/webapp/app/admin/configuration/configuration.service.ts new file mode 100644 index 000000000000..e239ed309748 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/configuration/configuration.service.ts @@ -0,0 +1,53 @@ +import { Injectable } from '@angular/core'; +import { Http, Response } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +@Injectable() +export class JhiConfigurationService { + + constructor(private http: Http) { + } + + get(): Observable { + return this.http.get('management/configprops').map((res: Response) => { + let properties: any[] = []; + + const propertiesObject = res.json(); + + for (let key in propertiesObject) { + if (propertiesObject.hasOwnProperty(key)) { + properties.push(propertiesObject[key]); + } + } + + return properties.sort((propertyA, propertyB) => { + return (propertyA.prefix === propertyB.prefix) ? 0 : + (propertyA.prefix < propertyB.prefix) ? -1 : 1; + }); + }); + } + + getEnv(): Observable { + return this.http.get('management/env').map((res: Response) => { + let properties: any = {}; + + const propertiesObject = res.json(); + + for (let key in propertiesObject) { + if (propertiesObject.hasOwnProperty(key)) { + let valsObject = propertiesObject[key]; + let vals: any[] = []; + + for (let valKey in valsObject) { + if (valsObject.hasOwnProperty(valKey)) { + vals.push({key: valKey, val: valsObject[valKey]}); + } + } + properties[key] = vals; + } + } + + return properties; + }); + } +} diff --git a/jhipster/src/main/webapp/app/admin/docs/docs.component.html b/jhipster/src/main/webapp/app/admin/docs/docs.component.html new file mode 100644 index 000000000000..30efbbb93ee5 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/docs/docs.component.html @@ -0,0 +1,2 @@ + diff --git a/jhipster/src/main/webapp/app/admin/docs/docs.component.ts b/jhipster/src/main/webapp/app/admin/docs/docs.component.ts new file mode 100644 index 000000000000..4bd5bf232962 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/docs/docs.component.ts @@ -0,0 +1,14 @@ +import { Component } from '@angular/core'; +import { JhiLanguageService } from 'ng-jhipster'; + +@Component({ + selector: 'jhi-docs', + templateUrl: './docs.component.html' +}) +export class JhiDocsComponent { + constructor ( + private jhiLanguageService: JhiLanguageService + ) { + this.jhiLanguageService.setLocations(['global']); + } +} diff --git a/jhipster/src/main/webapp/app/admin/docs/docs.route.ts b/jhipster/src/main/webapp/app/admin/docs/docs.route.ts new file mode 100644 index 000000000000..6e99618f1def --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/docs/docs.route.ts @@ -0,0 +1,12 @@ +import { Route } from '@angular/router'; + +import { UserRouteAccessService } from '../../shared'; +import { JhiDocsComponent } from './docs.component'; + +export const docsRoute: Route = { + path: 'docs', + component: JhiDocsComponent, + data: { + pageTitle: 'global.menu.admin.apidocs' + } +}; diff --git a/jhipster/src/main/webapp/app/admin/health/health-modal.component.html b/jhipster/src/main/webapp/app/admin/health/health-modal.component.html new file mode 100644 index 000000000000..1be6271f3ba3 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/health/health-modal.component.html @@ -0,0 +1,36 @@ + + + diff --git a/jhipster/src/main/webapp/app/admin/health/health-modal.component.ts b/jhipster/src/main/webapp/app/admin/health/health-modal.component.ts new file mode 100644 index 000000000000..1486f20dbf5e --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/health/health-modal.component.ts @@ -0,0 +1,37 @@ +import { Component } from '@angular/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; + +import { JhiHealthService } from './health.service'; + +@Component({ + selector: 'jhi-health-modal', + templateUrl: './health-modal.component.html' +}) +export class JhiHealthModalComponent { + + currentHealth: any; + + constructor(private healthService: JhiHealthService, public activeModal: NgbActiveModal) {} + + baseName(name) { + return this.healthService.getBaseName(name); + } + + subSystemName(name) { + return this.healthService.getSubSystemName(name); + } + + readableValue(value: number) { + if (this.currentHealth.name !== 'diskSpace') { + return value.toString(); + } + + // Should display storage space in an human readable unit + let val = value / 1073741824; + if (val > 1) { // Value + return val.toFixed(2) + ' GB'; + } else { + return (value / 1048576).toFixed(2) + ' MB'; + } + } +} diff --git a/jhipster/src/main/webapp/app/admin/health/health.component.html b/jhipster/src/main/webapp/app/admin/health/health.component.html new file mode 100644 index 000000000000..6fea72af966e --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/health/health.component.html @@ -0,0 +1,34 @@ +
+

+ Health Checks + +

+
+ + + + + + + + + + + + + + + +
Service NameStatusDetails
{{'health.indicator.' + baseName(health.name) | translate}} {{subSystemName(health.name)}} + + {{health.status}} + + + + + +
+
+
diff --git a/jhipster/src/main/webapp/app/admin/health/health.component.ts b/jhipster/src/main/webapp/app/admin/health/health.component.ts new file mode 100644 index 000000000000..ab1be4271f2b --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/health/health.component.ts @@ -0,0 +1,69 @@ +import { Component, OnInit } from '@angular/core'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { JhiLanguageService } from 'ng-jhipster'; + +import { JhiHealthService } from './health.service'; +import { JhiHealthModalComponent } from './health-modal.component'; + +@Component({ + selector: 'jhi-health', + templateUrl: './health.component.html', +}) +export class JhiHealthCheckComponent implements OnInit { + healthData: any; + updatingHealth: boolean; + + constructor( + private jhiLanguageService: JhiLanguageService, + private modalService: NgbModal, + private healthService: JhiHealthService + ) { + this.jhiLanguageService.setLocations(['health']); + + } + + ngOnInit() { + this.refresh(); + } + + baseName(name: string) { + return this.healthService.getBaseName(name); + } + + getBadgeClass(statusState) { + if (statusState === 'UP') { + return 'badge-success'; + } else { + return 'badge-danger'; + } + } + + refresh() { + this.updatingHealth = true; + + this.healthService.checkHealth().subscribe(health => { + this.healthData = this.healthService.transformHealthData(health); + this.updatingHealth = false; + }, error => { + if (error.status === 503) { + this.healthData = this.healthService.transformHealthData(error.json()); + this.updatingHealth = false; + } + }); + } + + showHealth(health: any) { + const modalRef = this.modalService.open(JhiHealthModalComponent); + modalRef.componentInstance.currentHealth = health; + modalRef.result.then((result) => { + // Left blank intentionally, nothing to do here + }, (reason) => { + // Left blank intentionally, nothing to do here + }); + } + + subSystemName(name: string) { + return this.healthService.getSubSystemName(name); + } + +} diff --git a/jhipster/src/main/webapp/app/admin/health/health.route.ts b/jhipster/src/main/webapp/app/admin/health/health.route.ts new file mode 100644 index 000000000000..b47d07a0c0c4 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/health/health.route.ts @@ -0,0 +1,12 @@ +import { Route } from '@angular/router'; + +import { UserRouteAccessService } from '../../shared'; +import { JhiHealthCheckComponent } from './health.component'; + +export const healthRoute: Route = { + path: 'jhi-health', + component: JhiHealthCheckComponent, + data: { + pageTitle: 'health.title' + } +}; diff --git a/jhipster/src/main/webapp/app/admin/health/health.service.ts b/jhipster/src/main/webapp/app/admin/health/health.service.ts new file mode 100644 index 000000000000..1bf00dab085c --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/health/health.service.ts @@ -0,0 +1,140 @@ +import { Injectable } from '@angular/core'; +import { Http, Response } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +@Injectable() +export class JhiHealthService { + + separator: string; + + constructor (private http: Http) { + this.separator = '.'; + } + + checkHealth(): Observable { + return this.http.get('management/health').map((res: Response) => res.json()); + } + + transformHealthData(data): any { + let response = []; + this.flattenHealthData(response, null, data); + return response; + } + + getBaseName(name): string { + if (name) { + let split = name.split('.'); + return split[0]; + } + } + + getSubSystemName(name): string { + if (name) { + let split = name.split('.'); + split.splice(0, 1); + let remainder = split.join('.'); + return remainder ? ' - ' + remainder : ''; + } + } + + /* private methods */ + private addHealthObject(result, isLeaf, healthObject, name): any { + + let status: any; + let error: any; + let healthData: any = { + 'name': name, + 'error': error, + 'status': status + }; + + let details = {}; + let hasDetails = false; + + for (let key in healthObject) { + if (healthObject.hasOwnProperty(key)) { + let value = healthObject[key]; + if (key === 'status' || key === 'error') { + healthData[key] = value; + } else { + if (!this.isHealthObject(value)) { + details[key] = value; + hasDetails = true; + } + } + } + } + + // Add the details + if (hasDetails) { + healthData.details = details; + } + + // Only add nodes if they provide additional information + if (isLeaf || hasDetails || healthData.error) { + result.push(healthData); + } + return healthData; + } + + private flattenHealthData (result, path, data): any { + for (let key in data) { + if (data.hasOwnProperty(key)) { + let value = data[key]; + if (this.isHealthObject(value)) { + if (this.hasSubSystem(value)) { + this.addHealthObject(result, false, value, this.getModuleName(path, key)); + this.flattenHealthData(result, this.getModuleName(path, key), value); + } else { + this.addHealthObject(result, true, value, this.getModuleName(path, key)); + } + } + } + } + + return result; + } + + private getModuleName (path, name): string { + let result; + if (path && name) { + result = path + this.separator + name; + } else if (path) { + result = path; + } else if (name) { + result = name; + } else { + result = ''; + } + return result; + } + + private hasSubSystem (healthObject): boolean { + let result = false; + + for (let key in healthObject) { + if (healthObject.hasOwnProperty(key)) { + let value = healthObject[key]; + if (value && value.status) { + result = true; + } + } + } + + return result; + } + + private isHealthObject (healthObject): boolean { + let result = false; + + for (let key in healthObject) { + if (healthObject.hasOwnProperty(key)) { + if (key === 'status') { + result = true; + } + } + } + + return result; + } +} diff --git a/jhipster/src/main/webapp/app/admin/index.ts b/jhipster/src/main/webapp/app/admin/index.ts new file mode 100644 index 000000000000..2273c8af127a --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/index.ts @@ -0,0 +1,29 @@ +export * from './audits/audits.component'; +export * from './audits/audits.service'; +export * from './audits/audits.route'; +export * from './audits/audit.model'; +export * from './audits/audit-data.model'; +export * from './configuration/configuration.component'; +export * from './configuration/configuration.service'; +export * from './configuration/configuration.route'; +export * from './docs/docs.component'; +export * from './docs/docs.route'; +export * from './health/health.component'; +export * from './health/health-modal.component'; +export * from './health/health.service'; +export * from './health/health.route'; +export * from './logs/logs.component'; +export * from './logs/logs.service'; +export * from './logs/logs.route'; +export * from './logs/log.model'; +export * from './metrics/metrics.component'; +export * from './metrics/metrics-modal.component'; +export * from './metrics/metrics.service'; +export * from './metrics/metrics.route'; +export * from './user-management/user-management-dialog.component'; +export * from './user-management/user-management-delete-dialog.component'; +export * from './user-management/user-management-detail.component'; +export * from './user-management/user-management.component'; +export * from './user-management/user-management.route'; +export * from './user-management/user-modal.service'; +export * from './admin.route'; diff --git a/jhipster/src/main/webapp/app/admin/logs/log.model.ts b/jhipster/src/main/webapp/app/admin/logs/log.model.ts new file mode 100644 index 000000000000..79cbd3480884 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/logs/log.model.ts @@ -0,0 +1,6 @@ +export class Log { + constructor( + public name: string, + public level: string + ) { } +} diff --git a/jhipster/src/main/webapp/app/admin/logs/logs.component.html b/jhipster/src/main/webapp/app/admin/logs/logs.component.html new file mode 100644 index 000000000000..242ef5563a9c --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/logs/logs.component.html @@ -0,0 +1,27 @@ +
+

Logs

+ +

There are {{ loggers.length }} loggers.

+ + Filter + + + + + + + + + + + + + +
NameLevel
{{logger.name | slice:0:140}} + + + + + +
+
diff --git a/jhipster/src/main/webapp/app/admin/logs/logs.component.ts b/jhipster/src/main/webapp/app/admin/logs/logs.component.ts new file mode 100644 index 000000000000..7655749018bc --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/logs/logs.component.ts @@ -0,0 +1,38 @@ +import { Component, OnInit } from '@angular/core'; +import { JhiLanguageService } from 'ng-jhipster'; + +import { Log } from './log.model'; +import { LogsService } from './logs.service'; + +@Component({ + selector: 'jhi-logs', + templateUrl: './logs.component.html', +}) +export class LogsComponent implements OnInit { + + loggers: Log[]; + filter: string; + orderProp: string; + reverse: boolean; + + constructor ( + private jhiLanguageService: JhiLanguageService, + private logsService: LogsService + ) { + this.filter = ''; + this.orderProp = 'name'; + this.reverse = false; + this.jhiLanguageService.setLocations(['logs']); + } + + ngOnInit() { + this.logsService.findAll().subscribe(loggers => this.loggers = loggers); + } + + changeLevel (name: string, level: string) { + let log = new Log(name, level); + this.logsService.changeLevel(log).subscribe(() => { + this.logsService.findAll().subscribe(loggers => this.loggers = loggers); + }); + } +} diff --git a/jhipster/src/main/webapp/app/admin/logs/logs.route.ts b/jhipster/src/main/webapp/app/admin/logs/logs.route.ts new file mode 100644 index 000000000000..1077f5da32a3 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/logs/logs.route.ts @@ -0,0 +1,12 @@ +import { Route } from '@angular/router'; + +import { UserRouteAccessService } from '../../shared'; +import { LogsComponent } from './logs.component'; + +export const logsRoute: Route = { + path: 'logs', + component: LogsComponent, + data: { + pageTitle: 'logs.title' + } +}; diff --git a/jhipster/src/main/webapp/app/admin/logs/logs.service.ts b/jhipster/src/main/webapp/app/admin/logs/logs.service.ts new file mode 100644 index 000000000000..c937c10a42e3 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/logs/logs.service.ts @@ -0,0 +1,18 @@ +import { Injectable } from '@angular/core'; +import { Http, Response } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +import { Log } from './log.model'; + +@Injectable() +export class LogsService { + constructor(private http: Http) { } + + changeLevel(log: Log): Observable { + return this.http.put('management/logs', log); + } + + findAll(): Observable { + return this.http.get('management/logs').map((res: Response) => res.json()); + } +} diff --git a/jhipster/src/main/webapp/app/admin/metrics/metrics-modal.component.html b/jhipster/src/main/webapp/app/admin/metrics/metrics-modal.component.html new file mode 100644 index 000000000000..b8f0391acf1a --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/metrics/metrics-modal.component.html @@ -0,0 +1,56 @@ + + + + diff --git a/jhipster/src/main/webapp/app/admin/metrics/metrics-modal.component.ts b/jhipster/src/main/webapp/app/admin/metrics/metrics-modal.component.ts new file mode 100644 index 000000000000..4a3034dd94b5 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/metrics/metrics-modal.component.ts @@ -0,0 +1,48 @@ +import { Component, OnInit } from '@angular/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; + +@Component({ + selector: 'jhi-metrics-modal', + templateUrl: './metrics-modal.component.html' +}) +export class JhiMetricsMonitoringModalComponent implements OnInit { + + threadDumpFilter: any; + threadDump: any; + threadDumpAll = 0; + threadDumpBlocked = 0; + threadDumpRunnable = 0; + threadDumpTimedWaiting = 0; + threadDumpWaiting = 0; + + constructor(public activeModal: NgbActiveModal) {} + + ngOnInit() { + this.threadDump.forEach((value) => { + if (value.threadState === 'RUNNABLE') { + this.threadDumpRunnable += 1; + } else if (value.threadState === 'WAITING') { + this.threadDumpWaiting += 1; + } else if (value.threadState === 'TIMED_WAITING') { + this.threadDumpTimedWaiting += 1; + } else if (value.threadState === 'BLOCKED') { + this.threadDumpBlocked += 1; + } + }); + + this.threadDumpAll = this.threadDumpRunnable + this.threadDumpWaiting + + this.threadDumpTimedWaiting + this.threadDumpBlocked; + } + + getBadgeClass (threadState) { + if (threadState === 'RUNNABLE') { + return 'badge-success'; + } else if (threadState === 'WAITING') { + return 'badge-info'; + } else if (threadState === 'TIMED_WAITING') { + return 'badge-warning'; + } else if (threadState === 'BLOCKED') { + return 'badge-danger'; + } + } +} diff --git a/jhipster/src/main/webapp/app/admin/metrics/metrics.component.html b/jhipster/src/main/webapp/app/admin/metrics/metrics.component.html new file mode 100644 index 000000000000..800ca21ef947 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/metrics/metrics.component.html @@ -0,0 +1,253 @@ +
+

+ Application Metrics + +

+ +

JVM Metrics

+
+
+ Memory +

Total Memory ({{metrics.gauges['jvm.memory.total.used'].value / 1000000 | number:'1.0-0'}}M / {{metrics.gauges['jvm.memory.total.max'].value / 1000000 | number:'1.0-0'}}M)

+ + {{metrics.gauges['jvm.memory.total.used'].value * 100 / metrics.gauges['jvm.memory.total.max'].value | number:'1.0-0'}}% + +

Heap Memory ({{metrics.gauges['jvm.memory.heap.used'].value / 1000000 | number:'1.0-0'}}M / {{metrics.gauges['jvm.memory.heap.max'].value / 1000000 | number:'1.0-0'}}M)

+ + {{metrics.gauges['jvm.memory.heap.used'].value * 100 / metrics.gauges['jvm.memory.heap.max'].value | number:'1.0-0'}}% + +

Non-Heap Memory ({{metrics.gauges['jvm.memory.non-heap.used'].value / 1000000 | number:'1.0-0'}}M / {{metrics.gauges['jvm.memory.non-heap.committed'].value / 1000000 | number:'1.0-0'}}M)

+ + {{metrics.gauges['jvm.memory.non-heap.used'].value * 100 / metrics.gauges['jvm.memory.non-heap.committed'].value | number:'1.0-0'}}% + +
+
+ Threads (Total: {{metrics.gauges['jvm.threads.count'].value}}) +

Runnable {{metrics.gauges['jvm.threads.runnable.count'].value}}

+ + {{metrics.gauges['jvm.threads.runnable.count'].value * 100 / metrics.gauges['jvm.threads.count'].value | number:'1.0-0'}}% + +

Timed Waiting ({{metrics.gauges['jvm.threads.timed_waiting.count'].value}})

+ + {{metrics.gauges['jvm.threads.timed_waiting.count'].value * 100 / metrics.gauges['jvm.threads.count'].value | number:'1.0-0'}}% + +

Waiting ({{metrics.gauges['jvm.threads.waiting.count'].value}})

+ + {{metrics.gauges['jvm.threads.waiting.count'].value * 100 / metrics.gauges['jvm.threads.count'].value | number:'1.0-0'}}% + +

Blocked ({{metrics.gauges['jvm.threads.blocked.count'].value}})

+ + {{metrics.gauges['jvm.threads.blocked.count'].value * 100 / metrics.gauges['jvm.threads.count'].value | number:'1.0-0'}}% + +
+
+ Garbage collections +
+
Mark Sweep count
+
{{metrics.gauges['jvm.garbage.PS-MarkSweep.count'].value}}
+
+
+
Mark Sweep time
+
{{metrics.gauges['jvm.garbage.PS-MarkSweep.time'].value}}ms
+
+
+
Scavenge count
+
{{metrics.gauges['jvm.garbage.PS-Scavenge.count'].value}}
+
+
+
Scavenge time
+
{{metrics.gauges['jvm.garbage.PS-Scavenge.time'].value}}ms
+
+
+
+
Updating...
+ +

HTTP requests (events per second)

+

+ Active requests {{metrics.counters['com.codahale.metrics.servlet.InstrumentedFilter.activeRequests'].count | number:'1.0-0'}} - Total requests {{metrics.timers['com.codahale.metrics.servlet.InstrumentedFilter.requests'].count | number:'1.0-0'}} +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CodeCountMeanAverage (1 min)Average (5 min)Average (15 min)
OK + + {{metrics.meters['com.codahale.metrics.servlet.InstrumentedFilter.responseCodes.ok'].count}} + + + {{metrics.meters['com.codahale.metrics.servlet.InstrumentedFilter.responseCodes.ok'].mean_rate | number:'1.0-2'}} + {{metrics.meters['com.codahale.metrics.servlet.InstrumentedFilter.responseCodes.ok'].m1_rate | number:'1.0-2'}} + {{metrics.meters['com.codahale.metrics.servlet.InstrumentedFilter.responseCodes.ok'].m5_rate | number:'1.0-2'}} + + {{metrics.meters['com.codahale.metrics.servlet.InstrumentedFilter.responseCodes.ok'].m15_rate | number:'1.0-2'}} +
Not Found + + {{metrics.meters['com.codahale.metrics.servlet.InstrumentedFilter.responseCodes.notFound'].count}} + + + {{metrics.meters['com.codahale.metrics.servlet.InstrumentedFilter.responseCodes.notFound'].mean_rate | number:'1.0-2'}} + + {{metrics.meters['com.codahale.metrics.servlet.InstrumentedFilter.responseCodes.notFound'].m1_rate | number:'1.0-2'}} + + {{metrics.meters['com.codahale.metrics.servlet.InstrumentedFilter.responseCodes.notFound'].m5_rate | number:'1.0-2'}} + + {{metrics.meters['com.codahale.metrics.servlet.InstrumentedFilter.responseCodes.notFound'].m15_rate | number:'1.0-2'}} +
Server error + + {{metrics.meters['com.codahale.metrics.servlet.InstrumentedFilter.responseCodes.serverError'].count}} + + + {{metrics.meters['com.codahale.metrics.servlet.InstrumentedFilter.responseCodes.serverError'].mean_rate | number:'1.0-2'}} + + {{metrics.meters['com.codahale.metrics.servlet.InstrumentedFilter.responseCodes.serverError'].m1_rate | number:'1.0-2'}} + + {{metrics.meters['com.codahale.metrics.servlet.InstrumentedFilter.responseCodes.serverError'].m5_rate | number:'1.0-2'}} + + {{metrics.meters['com.codahale.metrics.servlet.InstrumentedFilter.responseCodes.serverError'].m15_rate | number:'1.0-2'}} +
+
+ +

Services statistics (time in millisecond)

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Service nameCountMeanMinp50p75p95p99Max
{{entry.key}}{{entry.value.count}}{{entry.value.mean * 1000 | number:'1.0-0'}}{{entry.value.min * 1000 | number:'1.0-0'}}{{entry.value.p50 * 1000 | number:'1.0-0'}}{{entry.value.p75 * 1000 | number:'1.0-0'}}{{entry.value.p95 * 1000 | number:'1.0-0'}}{{entry.value.p99 * 1000 | number:'1.0-0'}}{{entry.value.max * 1000 | number:'1.0-0'}}
+
+ +

Cache statistics

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Cache nameCache HitsCache MissesCache GetsCache PutsCache RemovalsCache EvictionsCache Hit %Cache Miss %Average get time (µs)Average put time (µs)Average remove time (µs)
{{entry.key}}{{metrics.gauges[entry.key + '.cache-hits'].value}}{{metrics.gauges[entry.key + '.cache-misses'].value}}{{metrics.gauges[entry.key + '.cache-gets'].value}}{{metrics.gauges[entry.key + '.cache-puts'].value}}{{metrics.gauges[entry.key + '.cache-removals'].value}}{{metrics.gauges[entry.key + '.cache-evictions'].value}}{{metrics.gauges[entry.key + '.cache-hit-percentage'].value}}{{metrics.gauges[entry.key + '.cache-miss-percentage'].value }}{{metrics.gauges[entry.key + '.average-get-time'].value | number : '1.2-2' }}{{metrics.gauges[entry.key + '.average-put-time'].value | number : '1.2-2'}}{{metrics.gauges[entry.key + '.average-remove-time'].value | number : '1.2-2' }}
+
+ +

DataSource statistics (time in millisecond)

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Usage ({{metrics.gauges['HikariPool-1.pool.ActiveConnections'].value}} / {{metrics.gauges['HikariPool-1.pool.TotalConnections'].value}})CountMeanMinp50p75p95p99Max
+
+ + {{metrics.gauges['HikariPool-1.pool.ActiveConnections'].value * 100 / metrics.gauges['HikariPool-1.pool.TotalConnections'].value | number:'1.0-0'}}% + +
+
{{metrics.histograms['HikariPool-1.pool.Usage'].count}}{{metrics.histograms['HikariPool-1.pool.Usage'].mean | number:'1.0-2'}}{{metrics.histograms['HikariPool-1.pool.Usage'].min | number:'1.0-2'}}{{metrics.histograms['HikariPool-1.pool.Usage'].p50 | number:'1.0-2'}}{{metrics.histograms['HikariPool-1.pool.Usage'].p75 | number:'1.0-2'}}{{metrics.histograms['HikariPool-1.pool.Usage'].p95 | number:'1.0-2'}}{{metrics.histograms['HikariPool-1.pool.Usage'].p99 | number:'1.0-2'}}{{metrics.histograms['HikariPool-1.pool.Usage'].max | number:'1.0-2'}}
+
+
diff --git a/jhipster/src/main/webapp/app/admin/metrics/metrics.component.ts b/jhipster/src/main/webapp/app/admin/metrics/metrics.component.ts new file mode 100644 index 000000000000..21c39a3debcf --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/metrics/metrics.component.ts @@ -0,0 +1,74 @@ +import { Component, OnInit } from '@angular/core'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { JhiLanguageService } from 'ng-jhipster'; + +import { JhiMetricsMonitoringModalComponent } from './metrics-modal.component'; +import { JhiMetricsService } from './metrics.service'; + +@Component({ + selector: 'jhi-metrics', + templateUrl: './metrics.component.html', +}) +export class JhiMetricsMonitoringComponent implements OnInit { + metrics: any = {}; + cachesStats: any = {}; + servicesStats: any = {}; + updatingMetrics = true; + JCACHE_KEY: string ; + + constructor( + private jhiLanguageService: JhiLanguageService, + private modalService: NgbModal, + private metricsService: JhiMetricsService + ) { + this.JCACHE_KEY = 'jcache.statistics'; + this.jhiLanguageService.setLocations(['metrics']); + } + + ngOnInit() { + this.refresh(); + } + + refresh () { + this.updatingMetrics = true; + this.metricsService.getMetrics().subscribe((metrics) => { + this.metrics = metrics; + this.updatingMetrics = false; + this.servicesStats = {}; + this.cachesStats = {}; + Object.keys(metrics.timers).forEach((key) => { + let value = metrics.timers[key]; + if (key.indexOf('web.rest') !== -1 || key.indexOf('service') !== -1) { + this.servicesStats[key] = value; + } + }); + Object.keys(metrics.gauges).forEach((key) => { + if (key.indexOf('jcache.statistics') !== -1) { + let value = metrics.gauges[key].value; + // remove gets or puts + let index = key.lastIndexOf('.'); + let newKey = key.substr(0, index); + + // Keep the name of the domain + this.cachesStats[newKey] = { + 'name': this.JCACHE_KEY.length, + 'value': value + }; + } + }); + }); + } + + refreshThreadDumpData () { + this.metricsService.threadDump().subscribe((data) => { + const modalRef = this.modalService.open(JhiMetricsMonitoringModalComponent, { size: 'lg'}); + modalRef.componentInstance.threadDump = data; + modalRef.result.then((result) => { + // Left blank intentionally, nothing to do here + }, (reason) => { + // Left blank intentionally, nothing to do here + }); + }); + } + +} diff --git a/jhipster/src/main/webapp/app/admin/metrics/metrics.route.ts b/jhipster/src/main/webapp/app/admin/metrics/metrics.route.ts new file mode 100644 index 000000000000..cc13284dc8e2 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/metrics/metrics.route.ts @@ -0,0 +1,12 @@ +import { Route } from '@angular/router'; + +import { UserRouteAccessService } from '../../shared'; +import { JhiMetricsMonitoringComponent } from './metrics.component'; + +export const metricsRoute: Route = { + path: 'jhi-metrics', + component: JhiMetricsMonitoringComponent, + data: { + pageTitle: 'metrics.title' + } +}; diff --git a/jhipster/src/main/webapp/app/admin/metrics/metrics.service.ts b/jhipster/src/main/webapp/app/admin/metrics/metrics.service.ts new file mode 100644 index 000000000000..2b3194733902 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/metrics/metrics.service.ts @@ -0,0 +1,17 @@ +import { Injectable } from '@angular/core'; +import { Http, Response } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +@Injectable() +export class JhiMetricsService { + + constructor (private http: Http) {} + + getMetrics(): Observable { + return this.http.get('management/metrics').map((res: Response) => res.json()); + } + + threadDump(): Observable { + return this.http.get('management/dump').map((res: Response) => res.json()); + } +} diff --git a/jhipster/src/main/webapp/app/admin/user-management/user-management-delete-dialog.component.html b/jhipster/src/main/webapp/app/admin/user-management/user-management-delete-dialog.component.html new file mode 100644 index 000000000000..452f1612c8d0 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/user-management/user-management-delete-dialog.component.html @@ -0,0 +1,19 @@ +
+ + + +
diff --git a/jhipster/src/main/webapp/app/admin/user-management/user-management-delete-dialog.component.ts b/jhipster/src/main/webapp/app/admin/user-management/user-management-delete-dialog.component.ts new file mode 100644 index 000000000000..2c5c2a6a0135 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/user-management/user-management-delete-dialog.component.ts @@ -0,0 +1,63 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { NgbActiveModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { EventManager, JhiLanguageService } from 'ng-jhipster'; + +import { User, UserService } from '../../shared'; +import { UserModalService } from './user-modal.service'; + +@Component({ + selector: 'jhi-user-mgmt-delete-dialog', + templateUrl: './user-management-delete-dialog.component.html' +}) +export class UserMgmtDeleteDialogComponent { + + user: User; + + constructor( + private jhiLanguageService: JhiLanguageService, + private userService: UserService, + public activeModal: NgbActiveModal, + private eventManager: EventManager + ) { + this.jhiLanguageService.setLocations(['user-management']); + } + + clear () { + this.activeModal.dismiss('cancel'); + } + + confirmDelete (login) { + this.userService.delete(login).subscribe(response => { + this.eventManager.broadcast({ name: 'userListModification', + content: 'Deleted a user'}); + this.activeModal.dismiss(true); + }); + } + +} + +@Component({ + selector: 'jhi-user-delete-dialog', + template: '' +}) +export class UserDeleteDialogComponent implements OnInit, OnDestroy { + + modalRef: NgbModalRef; + routeSub: any; + + constructor ( + private route: ActivatedRoute, + private userModalService: UserModalService + ) {} + + ngOnInit() { + this.routeSub = this.route.params.subscribe(params => { + this.modalRef = this.userModalService.open(UserMgmtDeleteDialogComponent, params['login']); + }); + } + + ngOnDestroy() { + this.routeSub.unsubscribe(); + } +} diff --git a/jhipster/src/main/webapp/app/admin/user-management/user-management-detail.component.html b/jhipster/src/main/webapp/app/admin/user-management/user-management-detail.component.html new file mode 100644 index 000000000000..d5deb8fa24ea --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/user-management/user-management-detail.component.html @@ -0,0 +1,44 @@ +
+

+ User [{{user.login}}] +

+
+
Login
+
+ {{user.login}} + Deactivated + Activated +
+
First Name
+
{{user.firstName}}
+
Last Name
+
{{user.lastName}}
+
Email
+
{{user.email}}
+
Lang Key
+
{{user.langKey}}
+
Created By
+
{{user.createdBy}}
+
Created Date
+
{{user.createdDate | date:'dd/MM/yy HH:mm' }}
+
Last Modified By
+
{{user.lastModifiedBy}}
+
Last Modified Date
+
{{user.lastModifiedDate | date:'dd/MM/yy HH:mm'}}
+
Profiles
+
+
    +
  • + {{authority}} +
  • +
+
+
+ +
diff --git a/jhipster/src/main/webapp/app/admin/user-management/user-management-detail.component.ts b/jhipster/src/main/webapp/app/admin/user-management/user-management-detail.component.ts new file mode 100644 index 000000000000..564b1daf3d7c --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/user-management/user-management-detail.component.ts @@ -0,0 +1,40 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { JhiLanguageService } from 'ng-jhipster'; + +import { User, UserService } from '../../shared'; + +@Component({ + selector: 'jhi-user-mgmt-detail', + templateUrl: './user-management-detail.component.html' +}) +export class UserMgmtDetailComponent implements OnInit, OnDestroy { + + user: User; + private subscription: any; + + constructor( + private jhiLanguageService: JhiLanguageService, + private userService: UserService, + private route: ActivatedRoute + ) { + this.jhiLanguageService.setLocations(['user-management']); + } + + ngOnInit() { + this.subscription = this.route.params.subscribe(params => { + this.load(params['login']); + }); + } + + load (login) { + this.userService.find(login).subscribe(user => { + this.user = user; + }); + } + + ngOnDestroy() { + this.subscription.unsubscribe(); + } + +} diff --git a/jhipster/src/main/webapp/app/admin/user-management/user-management-dialog.component.html b/jhipster/src/main/webapp/app/admin/user-management/user-management-dialog.component.html new file mode 100644 index 000000000000..6359527df74e --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/user-management/user-management-dialog.component.html @@ -0,0 +1,112 @@ +
+ + + + +
diff --git a/jhipster/src/main/webapp/app/admin/user-management/user-management-dialog.component.ts b/jhipster/src/main/webapp/app/admin/user-management/user-management-dialog.component.ts new file mode 100644 index 000000000000..ebbf073df48d --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/user-management/user-management-dialog.component.ts @@ -0,0 +1,89 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; + +import { NgbActiveModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { EventManager, JhiLanguageService } from 'ng-jhipster'; + +import { UserModalService } from './user-modal.service'; +import { JhiLanguageHelper, User, UserService } from '../../shared'; + +@Component({ + selector: 'jhi-user-mgmt-dialog', + templateUrl: './user-management-dialog.component.html' +}) +export class UserMgmtDialogComponent implements OnInit { + + user: User; + languages: any[]; + authorities: any[]; + isSaving: Boolean; + + constructor ( + public activeModal: NgbActiveModal, + private languageHelper: JhiLanguageHelper, + private jhiLanguageService: JhiLanguageService, + private userService: UserService, + private eventManager: EventManager + ) {} + + ngOnInit() { + this.isSaving = false; + this.authorities = ['ROLE_USER', 'ROLE_ADMIN']; + this.languageHelper.getAll().then((languages) => { + this.languages = languages; + }); + this.jhiLanguageService.setLocations(['user-management']); + } + + clear() { + this.activeModal.dismiss('cancel'); + } + + save() { + this.isSaving = true; + if (this.user.id !== null) { + this.userService.update(this.user).subscribe(response => this.onSaveSuccess(response), () => this.onSaveError()); + } else { + this.userService.create(this.user).subscribe(response => this.onSaveSuccess(response), () => this.onSaveError()); + } + } + + private onSaveSuccess(result) { + this.eventManager.broadcast({ name: 'userListModification', content: 'OK' }); + this.isSaving = false; + this.activeModal.dismiss(result); + } + + private onSaveError() { + this.isSaving = false; + } +} + +@Component({ + selector: 'jhi-user-dialog', + template: '' +}) +export class UserDialogComponent implements OnInit, OnDestroy { + + modalRef: NgbModalRef; + routeSub: any; + + constructor ( + private route: ActivatedRoute, + private userModalService: UserModalService + ) {} + + ngOnInit() { + this.routeSub = this.route.params.subscribe(params => { + if ( params['login'] ) { + this.modalRef = this.userModalService.open(UserMgmtDialogComponent, params['login']); + } else { + this.modalRef = this.userModalService.open(UserMgmtDialogComponent); + } + }); + } + + ngOnDestroy() { + this.routeSub.unsubscribe(); + } +} diff --git a/jhipster/src/main/webapp/app/admin/user-management/user-management.component.html b/jhipster/src/main/webapp/app/admin/user-management/user-management.component.html new file mode 100644 index 000000000000..73fc18469aca --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/user-management/user-management.component.html @@ -0,0 +1,81 @@ +
+

+ Users + +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IDLogin Email Lang Key ProfilesCreated Date Last Modified By Last Modified Date
{{user.id}}{{user.login}}{{user.email}} + Deactivated + Activated + {{user.langKey}} +
+ {{ authority }} +
+
{{user.createdDate | date:'dd/MM/yy HH:mm'}}{{user.lastModifiedBy}}{{user.lastModifiedDate | date:'dd/MM/yy HH:mm'}} +
+ + + +
+
+
+
+
+ +
+
+ +
+
+
diff --git a/jhipster/src/main/webapp/app/admin/user-management/user-management.component.ts b/jhipster/src/main/webapp/app/admin/user-management/user-management.component.ts new file mode 100644 index 000000000000..213bb24923ce --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/user-management/user-management.component.ts @@ -0,0 +1,131 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Response } from '@angular/http'; +import { ActivatedRoute, Router } from '@angular/router'; +import { EventManager, PaginationUtil, ParseLinks, AlertService, JhiLanguageService } from 'ng-jhipster'; + +import { ITEMS_PER_PAGE, Principal, User, UserService } from '../../shared'; +import { PaginationConfig } from '../../blocks/config/uib-pagination.config'; + +@Component({ + selector: 'jhi-user-mgmt', + templateUrl: './user-management.component.html' +}) +export class UserMgmtComponent implements OnInit, OnDestroy { + + currentAccount: any; + users: User[]; + error: any; + success: any; + routeData: any; + links: any; + totalItems: any; + queryCount: any; + itemsPerPage: any; + page: any; + predicate: any; + previousPage: any; + reverse: any; + + constructor( + private jhiLanguageService: JhiLanguageService, + private userService: UserService, + private parseLinks: ParseLinks, + private alertService: AlertService, + private principal: Principal, + private eventManager: EventManager, private paginationUtil: PaginationUtil, + private paginationConfig: PaginationConfig, + private activatedRoute: ActivatedRoute, + private router: Router + ) { + this.itemsPerPage = ITEMS_PER_PAGE; + this.routeData = this.activatedRoute.data.subscribe(data => { + this.page = data['pagingParams'].page; + this.previousPage = data['pagingParams'].page; + this.reverse = data['pagingParams'].ascending; + this.predicate = data['pagingParams'].predicate; + }); + this.jhiLanguageService.setLocations(['user-management']); + } + + ngOnInit() { + this.principal.identity().then((account) => { + this.currentAccount = account; + this.loadAll(); + this.registerChangeInUsers(); + }); + } + + ngOnDestroy() { + this.routeData.unsubscribe(); + } + + registerChangeInUsers() { + this.eventManager.subscribe('userListModification', (response) => this.loadAll()); + } + + setActive (user, isActivated) { + user.activated = isActivated; + + this.userService.update(user).subscribe( + response => { + if (response.status === 200) { + this.error = null; + this.success = 'OK'; + this.loadAll(); + } else { + this.success = null; + this.error = 'ERROR'; + } + }); + } + + loadAll () { + this.userService.query({ + page: this.page - 1, + size: this.itemsPerPage, + sort: this.sort()}).subscribe( + (res: Response) => this.onSuccess(res.json(), res.headers), + (res: Response) => this.onError(res.json()) + ); + } + + trackIdentity (index, item: User) { + return item.id; + } + + sort () { + let result = [this.predicate + ',' + (this.reverse ? 'asc' : 'desc')]; + if (this.predicate !== 'id') { + result.push('id'); + } + return result; + } + + loadPage (page: number) { + if (page !== this.previousPage) { + this.previousPage = page; + this.transition(); + } + } + + transition () { + this.router.navigate(['/user-management'], { queryParams: + { + page: this.page, + sort: this.predicate + ',' + (this.reverse ? 'asc' : 'desc') + } + }); + this.loadAll(); + } + + private onSuccess(data, headers) { + this.links = this.parseLinks.parse(headers.get('link')); + this.totalItems = headers.get('X-Total-Count'); + this.queryCount = this.totalItems; + this.users = data; + } + + private onError(error) { + this.alertService.error(error.error, error.message, null); + } +} diff --git a/jhipster/src/main/webapp/app/admin/user-management/user-management.route.ts b/jhipster/src/main/webapp/app/admin/user-management/user-management.route.ts new file mode 100644 index 000000000000..6aca0cdeb1c2 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/user-management/user-management.route.ts @@ -0,0 +1,77 @@ +import { Injectable } from '@angular/core'; +import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot, Routes, CanActivate } from '@angular/router'; + +import { UserRouteAccessService } from '../../shared'; +import { PaginationUtil } from 'ng-jhipster'; + +import { UserMgmtComponent } from './user-management.component'; +import { UserMgmtDetailComponent } from './user-management-detail.component'; +import { UserDialogComponent } from './user-management-dialog.component'; +import { UserDeleteDialogComponent } from './user-management-delete-dialog.component'; + +import { Principal } from '../../shared'; + + +@Injectable() +export class UserResolve implements CanActivate { + + constructor(private principal: Principal) { } + + canActivate() { + return this.principal.identity().then(account => this.principal.hasAnyAuthority(['ROLE_ADMIN'])); + } +} + +@Injectable() +export class UserResolvePagingParams implements Resolve { + + constructor(private paginationUtil: PaginationUtil) {} + + resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { + let page = route.queryParams['page'] ? route.queryParams['page'] : '1'; + let sort = route.queryParams['sort'] ? route.queryParams['sort'] : 'id,asc'; + return { + page: this.paginationUtil.parsePage(page), + predicate: this.paginationUtil.parsePredicate(sort), + ascending: this.paginationUtil.parseAscending(sort) + }; + } +} + +export const userMgmtRoute: Routes = [ + { + path: 'user-management', + component: UserMgmtComponent, + resolve: { + 'pagingParams': UserResolvePagingParams + }, + data: { + pageTitle: 'userManagement.home.title' + } + }, + { + path: 'user-management/:login', + component: UserMgmtDetailComponent, + data: { + pageTitle: 'userManagement.home.title' + } + } +]; + +export const userDialogRoute: Routes = [ + { + path: 'user-management-new', + component: UserDialogComponent, + outlet: 'popup' + }, + { + path: 'user-management/:login/edit', + component: UserDialogComponent, + outlet: 'popup' + }, + { + path: 'user-management/:login/delete', + component: UserDeleteDialogComponent, + outlet: 'popup' + } +]; diff --git a/jhipster/src/main/webapp/app/admin/user-management/user-modal.service.ts b/jhipster/src/main/webapp/app/admin/user-management/user-modal.service.ts new file mode 100644 index 000000000000..006bc2a53c1d --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/user-management/user-modal.service.ts @@ -0,0 +1,42 @@ +import { Injectable, Component } from '@angular/core'; +import { Router } from '@angular/router'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; + +import { UserMgmtDialogComponent } from './user-management-dialog.component'; +import { User, UserService } from '../../shared'; + +@Injectable() +export class UserModalService { + private isOpen = false; + constructor ( + private modalService: NgbModal, + private router: Router, + private userService: UserService + ) {} + + open (component: Component, login?: string): NgbModalRef { + if (this.isOpen) { + return; + } + this.isOpen = true; + + if (login) { + this.userService.find(login).subscribe(user => this.userModalRef(component, user)); + } else { + return this.userModalRef(component, new User()); + } + } + + userModalRef(component: Component, user: User): NgbModalRef { + let modalRef = this.modalService.open(component, { size: 'lg', backdrop: 'static'}); + modalRef.componentInstance.user = user; + modalRef.result.then(result => { + this.router.navigate([{ outlets: { popup: null }}], { replaceUrl: true }); + this.isOpen = false; + }, (reason) => { + this.router.navigate([{ outlets: { popup: null }}], { replaceUrl: true }); + this.isOpen = false; + }); + return modalRef; + } +} diff --git a/jhipster/src/main/webapp/app/app.constants.ts b/jhipster/src/main/webapp/app/app.constants.ts new file mode 100644 index 000000000000..8f53b6f2e830 --- /dev/null +++ b/jhipster/src/main/webapp/app/app.constants.ts @@ -0,0 +1,7 @@ +// DO NOT EDIT THIS FILE, EDIT THE WEBPACK COMMON CONFIG INSTEAD, WHICH WILL MODIFY THIS FILE +let _VERSION = '0.0.0'; // This value will be overwritten by webpack +let _DEBUG_INFO_ENABLED = true; // This value will be overwritten by webpack +/* @toreplace VERSION */ +/* @toreplace DEBUG_INFO_ENABLED */ +export const VERSION = _VERSION; +export const DEBUG_INFO_ENABLED = _DEBUG_INFO_ENABLED; diff --git a/jhipster/src/main/webapp/app/app.main.ts b/jhipster/src/main/webapp/app/app.main.ts new file mode 100644 index 000000000000..301e5ffb81f3 --- /dev/null +++ b/jhipster/src/main/webapp/app/app.main.ts @@ -0,0 +1,11 @@ +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { ProdConfig } from './blocks/config/prod.config'; +import { BaeldungAppModule } from './app.module'; + +ProdConfig(); + +if (module['hot']) { + module['hot'].accept(); +} + +platformBrowserDynamic().bootstrapModule(BaeldungAppModule); diff --git a/jhipster/src/main/webapp/app/app.module.ts b/jhipster/src/main/webapp/app/app.module.ts new file mode 100644 index 000000000000..dc82c6cdce88 --- /dev/null +++ b/jhipster/src/main/webapp/app/app.module.ts @@ -0,0 +1,58 @@ +import './vendor.ts'; + +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; +import { BrowserModule } from '@angular/platform-browser'; +import { Ng2Webstorage } from 'ng2-webstorage'; + +import { BaeldungSharedModule, UserRouteAccessService } from './shared'; +import { BaeldungHomeModule } from './home/home.module'; +import { BaeldungAdminModule } from './admin/admin.module'; +import { BaeldungAccountModule } from './account/account.module'; +import { BaeldungEntityModule } from './entities/entity.module'; + +import { LayoutRoutingModule } from './layouts'; +import { customHttpProvider } from './blocks/interceptor/http.provider'; +import { PaginationConfig } from './blocks/config/uib-pagination.config'; + +import { + JhiMainComponent, + NavbarComponent, + FooterComponent, + ProfileService, + PageRibbonComponent, + ActiveMenuDirective, + ErrorComponent +} from './layouts'; + + +@NgModule({ + imports: [ + BrowserModule, + LayoutRoutingModule, + Ng2Webstorage.forRoot({ prefix: 'jhi', separator: '-'}), + BaeldungSharedModule, + BaeldungHomeModule, + BaeldungAdminModule, + BaeldungAccountModule, + BaeldungEntityModule + ], + declarations: [ + JhiMainComponent, + NavbarComponent, + ErrorComponent, + PageRibbonComponent, + ActiveMenuDirective, + FooterComponent + ], + providers: [ + ProfileService, + { provide: Window, useValue: window }, + { provide: Document, useValue: document }, + customHttpProvider(), + PaginationConfig, + UserRouteAccessService + ], + bootstrap: [ JhiMainComponent ] +}) +export class BaeldungAppModule {} diff --git a/jhipster/src/main/webapp/app/app.route.ts b/jhipster/src/main/webapp/app/app.route.ts new file mode 100644 index 000000000000..bfddd9bb7e3b --- /dev/null +++ b/jhipster/src/main/webapp/app/app.route.ts @@ -0,0 +1,9 @@ +import { Route } from '@angular/router'; + +import { NavbarComponent } from './layouts'; + +export const navbarRoute: Route = { + path: '', + component: NavbarComponent, + outlet: 'navbar' + }; diff --git a/jhipster/src/main/webapp/app/blocks/config/prod.config.ts b/jhipster/src/main/webapp/app/blocks/config/prod.config.ts new file mode 100644 index 000000000000..cc0050de2790 --- /dev/null +++ b/jhipster/src/main/webapp/app/blocks/config/prod.config.ts @@ -0,0 +1,9 @@ +import { enableProdMode } from '@angular/core'; +import { DEBUG_INFO_ENABLED } from '../../app.constants'; + +export function ProdConfig() { + // disable debug data on prod profile to improve performance + if (!DEBUG_INFO_ENABLED) { + enableProdMode(); + } +} diff --git a/jhipster/src/main/webapp/app/blocks/config/uib-pagination.config.ts b/jhipster/src/main/webapp/app/blocks/config/uib-pagination.config.ts new file mode 100644 index 000000000000..245e1f6bf141 --- /dev/null +++ b/jhipster/src/main/webapp/app/blocks/config/uib-pagination.config.ts @@ -0,0 +1,13 @@ +import { ITEMS_PER_PAGE } from '../../shared'; +import { Injectable } from '@angular/core'; +import { NgbPaginationConfig} from '@ng-bootstrap/ng-bootstrap'; + +@Injectable() +export class PaginationConfig { + constructor(private config: NgbPaginationConfig) { + config.boundaryLinks = true; + config.maxSize = 5; + config.pageSize = ITEMS_PER_PAGE; + config.size = 'sm'; + } +} diff --git a/jhipster/src/main/webapp/app/blocks/interceptor/auth-expired.interceptor.ts b/jhipster/src/main/webapp/app/blocks/interceptor/auth-expired.interceptor.ts new file mode 100644 index 000000000000..49f517148cb0 --- /dev/null +++ b/jhipster/src/main/webapp/app/blocks/interceptor/auth-expired.interceptor.ts @@ -0,0 +1,34 @@ +import { HttpInterceptor } from 'ng-jhipster'; +import { RequestOptionsArgs, Response } from '@angular/http'; +import { Observable } from 'rxjs/Observable'; +import { Injector } from '@angular/core'; +import { AuthService } from '../../shared/auth/auth.service'; +import { Principal } from '../../shared/auth/principal.service'; +import { AuthServerProvider } from '../../shared/auth/auth-jwt.service'; + +export class AuthExpiredInterceptor extends HttpInterceptor { + + constructor(private injector: Injector) { + super(); + } + + requestIntercept(options?: RequestOptionsArgs): RequestOptionsArgs { + return options; + } + + responseIntercept(observable: Observable): Observable { + let self = this; + + return > observable.catch((error, source) => { + if (error.status === 401) { + let principal: Principal = self.injector.get(Principal); + + if (principal.isAuthenticated()) { + let auth: AuthService = self.injector.get(AuthService); + auth.authorize(true); + } + } + return Observable.throw(error); + }); + } +} diff --git a/jhipster/src/main/webapp/app/blocks/interceptor/auth.interceptor.ts b/jhipster/src/main/webapp/app/blocks/interceptor/auth.interceptor.ts new file mode 100644 index 000000000000..cf9028411602 --- /dev/null +++ b/jhipster/src/main/webapp/app/blocks/interceptor/auth.interceptor.ts @@ -0,0 +1,27 @@ +import { Observable } from 'rxjs/Observable'; +import { RequestOptionsArgs, Response } from '@angular/http'; +import { LocalStorageService, SessionStorageService } from 'ng2-webstorage'; +import { HttpInterceptor } from 'ng-jhipster'; + +export class AuthInterceptor extends HttpInterceptor { + + constructor( + private localStorage: LocalStorageService, + private sessionStorage: SessionStorageService + ) { + super(); + } + + requestIntercept(options?: RequestOptionsArgs): RequestOptionsArgs { + let token = this.localStorage.retrieve('authenticationToken') || this.sessionStorage.retrieve('authenticationToken'); + if (!!token) { + options.headers.append('Authorization', 'Bearer ' + token); + } + return options; + } + + responseIntercept(observable: Observable): Observable { + return observable; // by pass + } + +} diff --git a/jhipster/src/main/webapp/app/blocks/interceptor/errorhandler.interceptor.ts b/jhipster/src/main/webapp/app/blocks/interceptor/errorhandler.interceptor.ts new file mode 100644 index 000000000000..6e22557d051b --- /dev/null +++ b/jhipster/src/main/webapp/app/blocks/interceptor/errorhandler.interceptor.ts @@ -0,0 +1,24 @@ +import { HttpInterceptor, EventManager } from 'ng-jhipster'; +import { RequestOptionsArgs, Response } from '@angular/http'; +import { Observable } from 'rxjs/Observable'; + +export class ErrorHandlerInterceptor extends HttpInterceptor { + + constructor(private eventManager: EventManager) { + super(); + } + + requestIntercept(options?: RequestOptionsArgs): RequestOptionsArgs { + return options; + } + + responseIntercept(observable: Observable): Observable { + return > observable.catch(error => { + if (!(error.status === 401 && (error.text() === '' || + (error.json().path && error.json().path.indexOf('/api/account') === 0 )))) { + this.eventManager.broadcast( {name: 'baeldungApp.httpError', content: error}); + } + return Observable.throw(error); + }); + } +} diff --git a/jhipster/src/main/webapp/app/blocks/interceptor/http.provider.ts b/jhipster/src/main/webapp/app/blocks/interceptor/http.provider.ts new file mode 100644 index 000000000000..a9689662a355 --- /dev/null +++ b/jhipster/src/main/webapp/app/blocks/interceptor/http.provider.ts @@ -0,0 +1,45 @@ +import { Injector } from '@angular/core'; +import { Http, XHRBackend, RequestOptions } from '@angular/http'; +import { EventManager, InterceptableHttp } from 'ng-jhipster'; + +import { AuthInterceptor } from './auth.interceptor'; +import { LocalStorageService, SessionStorageService } from 'ng2-webstorage'; +import { AuthExpiredInterceptor } from './auth-expired.interceptor'; +import { ErrorHandlerInterceptor } from './errorhandler.interceptor'; +import { NotificationInterceptor } from './notification.interceptor'; + +export function interceptableFactory( + backend: XHRBackend, + defaultOptions: RequestOptions, + localStorage: LocalStorageService, + sessionStorage: SessionStorageService, + injector: Injector, + eventManager: EventManager +) { + return new InterceptableHttp( + backend, + defaultOptions, + [ + new AuthInterceptor(localStorage, sessionStorage), + new AuthExpiredInterceptor(injector), + // Other interceptors can be added here + new ErrorHandlerInterceptor(eventManager), + new NotificationInterceptor() + ] + ); +}; + +export function customHttpProvider() { + return { + provide: Http, + useFactory: interceptableFactory, + deps: [ + XHRBackend, + RequestOptions, + LocalStorageService, + SessionStorageService, + Injector, + EventManager + ] + }; +}; diff --git a/jhipster/src/main/webapp/app/blocks/interceptor/notification.interceptor.ts b/jhipster/src/main/webapp/app/blocks/interceptor/notification.interceptor.ts new file mode 100644 index 000000000000..ebee7688c54d --- /dev/null +++ b/jhipster/src/main/webapp/app/blocks/interceptor/notification.interceptor.ts @@ -0,0 +1,33 @@ +import { HttpInterceptor } from 'ng-jhipster'; +import { RequestOptionsArgs, Response } from '@angular/http'; +import { Observable } from 'rxjs/Observable'; + +export class NotificationInterceptor extends HttpInterceptor { + + constructor() { + super(); + } + + requestIntercept(options?: RequestOptionsArgs): RequestOptionsArgs { + return options; + } + + responseIntercept(observable: Observable): Observable { + return > observable.catch((error) => { + let arr = Array.from(error.headers._headers); + let headers = []; + let i; + for (i = 0; i < arr.length; i++) { + if (arr[i][0].endsWith('app-alert') || arr[i][0].endsWith('app-params')) { + headers.push(arr[i][0]); + } + } + headers.sort(); + let alertKey = headers.length >= 1 ? error.headers.get(headers[0]) : null; + if (typeof alertKey === 'string') { + // AlertService.success(alertKey, { param: response.headers(headers[1])}); + } + return Observable.throw(error); + }); + } +} diff --git a/jhipster/src/main/webapp/app/entities/comment/comment-delete-dialog.component.html b/jhipster/src/main/webapp/app/entities/comment/comment-delete-dialog.component.html new file mode 100644 index 000000000000..63540ec6bdf9 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/comment/comment-delete-dialog.component.html @@ -0,0 +1,19 @@ +
+ + + +
diff --git a/jhipster/src/main/webapp/app/entities/comment/comment-delete-dialog.component.ts b/jhipster/src/main/webapp/app/entities/comment/comment-delete-dialog.component.ts new file mode 100644 index 000000000000..29ef818ddfd5 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/comment/comment-delete-dialog.component.ts @@ -0,0 +1,67 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; + +import { NgbActiveModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { EventManager, JhiLanguageService } from 'ng-jhipster'; + +import { Comment } from './comment.model'; +import { CommentPopupService } from './comment-popup.service'; +import { CommentService } from './comment.service'; + +@Component({ + selector: 'jhi-comment-delete-dialog', + templateUrl: './comment-delete-dialog.component.html' +}) +export class CommentDeleteDialogComponent { + + comment: Comment; + + constructor( + private jhiLanguageService: JhiLanguageService, + private commentService: CommentService, + public activeModal: NgbActiveModal, + private eventManager: EventManager + ) { + this.jhiLanguageService.setLocations(['comment']); + } + + clear () { + this.activeModal.dismiss('cancel'); + } + + confirmDelete (id: number) { + this.commentService.delete(id).subscribe(response => { + this.eventManager.broadcast({ + name: 'commentListModification', + content: 'Deleted an comment' + }); + this.activeModal.dismiss(true); + }); + } +} + +@Component({ + selector: 'jhi-comment-delete-popup', + template: '' +}) +export class CommentDeletePopupComponent implements OnInit, OnDestroy { + + modalRef: NgbModalRef; + routeSub: any; + + constructor ( + private route: ActivatedRoute, + private commentPopupService: CommentPopupService + ) {} + + ngOnInit() { + this.routeSub = this.route.params.subscribe(params => { + this.modalRef = this.commentPopupService + .open(CommentDeleteDialogComponent, params['id']); + }); + } + + ngOnDestroy() { + this.routeSub.unsubscribe(); + } +} diff --git a/jhipster/src/main/webapp/app/entities/comment/comment-detail.component.html b/jhipster/src/main/webapp/app/entities/comment/comment-detail.component.html new file mode 100644 index 000000000000..add5b9e20830 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/comment/comment-detail.component.html @@ -0,0 +1,35 @@ + +
+

Comment {{comment.id}}

+
+ +
+
Text
+
+ {{comment.text}} +
+
Creation Date
+
+ {{comment.creationDate | date:'mediumDate'}} +
+
Post
+
+ +
+
+ + + + +
diff --git a/jhipster/src/main/webapp/app/entities/comment/comment-detail.component.ts b/jhipster/src/main/webapp/app/entities/comment/comment-detail.component.ts new file mode 100644 index 000000000000..c2bf7fce0fd6 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/comment/comment-detail.component.ts @@ -0,0 +1,43 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { JhiLanguageService } from 'ng-jhipster'; +import { Comment } from './comment.model'; +import { CommentService } from './comment.service'; + +@Component({ + selector: 'jhi-comment-detail', + templateUrl: './comment-detail.component.html' +}) +export class CommentDetailComponent implements OnInit, OnDestroy { + + comment: Comment; + private subscription: any; + + constructor( + private jhiLanguageService: JhiLanguageService, + private commentService: CommentService, + private route: ActivatedRoute + ) { + this.jhiLanguageService.setLocations(['comment']); + } + + ngOnInit() { + this.subscription = this.route.params.subscribe(params => { + this.load(params['id']); + }); + } + + load (id) { + this.commentService.find(id).subscribe(comment => { + this.comment = comment; + }); + } + previousState() { + window.history.back(); + } + + ngOnDestroy() { + this.subscription.unsubscribe(); + } + +} diff --git a/jhipster/src/main/webapp/app/entities/comment/comment-dialog.component.html b/jhipster/src/main/webapp/app/entities/comment/comment-dialog.component.html new file mode 100644 index 000000000000..7e13c3317721 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/comment/comment-dialog.component.html @@ -0,0 +1,76 @@ + + +
+ + + + +
diff --git a/jhipster/src/main/webapp/app/entities/comment/comment-dialog.component.ts b/jhipster/src/main/webapp/app/entities/comment/comment-dialog.component.ts new file mode 100644 index 000000000000..1a30305c965a --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/comment/comment-dialog.component.ts @@ -0,0 +1,107 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { Response } from '@angular/http'; + +import { NgbActiveModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { EventManager, AlertService, JhiLanguageService } from 'ng-jhipster'; + +import { Comment } from './comment.model'; +import { CommentPopupService } from './comment-popup.service'; +import { CommentService } from './comment.service'; +import { Post, PostService } from '../post'; +@Component({ + selector: 'jhi-comment-dialog', + templateUrl: './comment-dialog.component.html' +}) +export class CommentDialogComponent implements OnInit { + + comment: Comment; + authorities: any[]; + isSaving: boolean; + + posts: Post[]; + constructor( + public activeModal: NgbActiveModal, + private jhiLanguageService: JhiLanguageService, + private alertService: AlertService, + private commentService: CommentService, + private postService: PostService, + private eventManager: EventManager + ) { + this.jhiLanguageService.setLocations(['comment']); + } + + ngOnInit() { + this.isSaving = false; + this.authorities = ['ROLE_USER', 'ROLE_ADMIN']; + this.postService.query().subscribe( + (res: Response) => { this.posts = res.json(); }, (res: Response) => this.onError(res.json())); + } + clear () { + this.activeModal.dismiss('cancel'); + } + + save () { + this.isSaving = true; + if (this.comment.id !== undefined) { + this.commentService.update(this.comment) + .subscribe((res: Comment) => + this.onSaveSuccess(res), (res: Response) => this.onSaveError(res.json())); + } else { + this.commentService.create(this.comment) + .subscribe((res: Comment) => + this.onSaveSuccess(res), (res: Response) => this.onSaveError(res.json())); + } + } + + private onSaveSuccess (result: Comment) { + this.eventManager.broadcast({ name: 'commentListModification', content: 'OK'}); + this.isSaving = false; + this.activeModal.dismiss(result); + } + + private onSaveError (error) { + this.isSaving = false; + this.onError(error); + } + + private onError (error) { + this.alertService.error(error.message, null, null); + } + + trackPostById(index: number, item: Post) { + return item.id; + } +} + +@Component({ + selector: 'jhi-comment-popup', + template: '' +}) +export class CommentPopupComponent implements OnInit, OnDestroy { + + modalRef: NgbModalRef; + routeSub: any; + + constructor ( + private route: ActivatedRoute, + private commentPopupService: CommentPopupService + ) {} + + ngOnInit() { + this.routeSub = this.route.params.subscribe(params => { + if ( params['id'] ) { + this.modalRef = this.commentPopupService + .open(CommentDialogComponent, params['id']); + } else { + this.modalRef = this.commentPopupService + .open(CommentDialogComponent); + } + + }); + } + + ngOnDestroy() { + this.routeSub.unsubscribe(); + } +} diff --git a/jhipster/src/main/webapp/app/entities/comment/comment-popup.service.ts b/jhipster/src/main/webapp/app/entities/comment/comment-popup.service.ts new file mode 100644 index 000000000000..a1c0ce11bf30 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/comment/comment-popup.service.ts @@ -0,0 +1,50 @@ +import { Injectable, Component } from '@angular/core'; +import { Router } from '@angular/router'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { Comment } from './comment.model'; +import { CommentService } from './comment.service'; +@Injectable() +export class CommentPopupService { + private isOpen = false; + constructor ( + private modalService: NgbModal, + private router: Router, + private commentService: CommentService + + ) {} + + open (component: Component, id?: number | any): NgbModalRef { + if (this.isOpen) { + return; + } + this.isOpen = true; + + if (id) { + this.commentService.find(id).subscribe(comment => { + if (comment.creationDate) { + comment.creationDate = { + year: comment.creationDate.getFullYear(), + month: comment.creationDate.getMonth() + 1, + day: comment.creationDate.getDate() + }; + } + this.commentModalRef(component, comment); + }); + } else { + return this.commentModalRef(component, new Comment()); + } + } + + commentModalRef(component: Component, comment: Comment): NgbModalRef { + let modalRef = this.modalService.open(component, { size: 'lg', backdrop: 'static'}); + modalRef.componentInstance.comment = comment; + modalRef.result.then(result => { + this.router.navigate([{ outlets: { popup: null }}], { replaceUrl: true }); + this.isOpen = false; + }, (reason) => { + this.router.navigate([{ outlets: { popup: null }}], { replaceUrl: true }); + this.isOpen = false; + }); + return modalRef; + } +} diff --git a/jhipster/src/main/webapp/app/entities/comment/comment.component.html b/jhipster/src/main/webapp/app/entities/comment/comment.component.html new file mode 100644 index 000000000000..1efd1ce37991 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/comment/comment.component.html @@ -0,0 +1,64 @@ +
+

+ Comments + +

+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + +
ID Text Creation Date Post
{{comment.id}}{{comment.text}}{{comment.creationDate | date:'mediumDate'}} + + +
+ + + +
+
+
+
diff --git a/jhipster/src/main/webapp/app/entities/comment/comment.component.ts b/jhipster/src/main/webapp/app/entities/comment/comment.component.ts new file mode 100644 index 000000000000..4e009d477480 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/comment/comment.component.ts @@ -0,0 +1,110 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Response } from '@angular/http'; +import { ActivatedRoute, Router } from '@angular/router'; +import { Subscription } from 'rxjs/Rx'; +import { EventManager, ParseLinks, PaginationUtil, JhiLanguageService, AlertService } from 'ng-jhipster'; + +import { Comment } from './comment.model'; +import { CommentService } from './comment.service'; +import { ITEMS_PER_PAGE, Principal } from '../../shared'; +import { PaginationConfig } from '../../blocks/config/uib-pagination.config'; + +@Component({ + selector: 'jhi-comment', + templateUrl: './comment.component.html' +}) +export class CommentComponent implements OnInit, OnDestroy { + + comments: Comment[]; + currentAccount: any; + eventSubscriber: Subscription; + itemsPerPage: number; + links: any; + page: any; + predicate: any; + queryCount: any; + reverse: any; + totalItems: number; + + constructor( + private jhiLanguageService: JhiLanguageService, + private commentService: CommentService, + private alertService: AlertService, + private eventManager: EventManager, + private parseLinks: ParseLinks, + private principal: Principal + ) { + this.comments = []; + this.itemsPerPage = ITEMS_PER_PAGE; + this.page = 0; + this.links = { + last: 0 + }; + this.predicate = 'id'; + this.reverse = true; + this.jhiLanguageService.setLocations(['comment']); + } + + loadAll () { + this.commentService.query({ + page: this.page, + size: this.itemsPerPage, + sort: this.sort() + }).subscribe( + (res: Response) => this.onSuccess(res.json(), res.headers), + (res: Response) => this.onError(res.json()) + ); + } + + reset () { + this.page = 0; + this.comments = []; + this.loadAll(); + } + + loadPage(page) { + this.page = page; + this.loadAll(); + } + ngOnInit() { + this.loadAll(); + this.principal.identity().then((account) => { + this.currentAccount = account; + }); + this.registerChangeInComments(); + } + + ngOnDestroy() { + this.eventManager.destroy(this.eventSubscriber); + } + + trackId (index: number, item: Comment) { + return item.id; + } + + + + registerChangeInComments() { + this.eventSubscriber = this.eventManager.subscribe('commentListModification', (response) => this.reset()); + } + + sort () { + let result = [this.predicate + ',' + (this.reverse ? 'asc' : 'desc')]; + if (this.predicate !== 'id') { + result.push('id'); + } + return result; + } + + private onSuccess(data, headers) { + this.links = this.parseLinks.parse(headers.get('link')); + this.totalItems = headers.get('X-Total-Count'); + for (let i = 0; i < data.length; i++) { + this.comments.push(data[i]); + } + } + + private onError (error) { + this.alertService.error(error.message, null, null); + } +} diff --git a/jhipster/src/main/webapp/app/entities/comment/comment.model.ts b/jhipster/src/main/webapp/app/entities/comment/comment.model.ts new file mode 100644 index 000000000000..66df7d0b34e9 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/comment/comment.model.ts @@ -0,0 +1,10 @@ +import { Post } from '../post'; +export class Comment { + constructor( + public id?: number, + public text?: string, + public creationDate?: any, + public post?: Post, + ) { + } +} diff --git a/jhipster/src/main/webapp/app/entities/comment/comment.module.ts b/jhipster/src/main/webapp/app/entities/comment/comment.module.ts new file mode 100644 index 000000000000..1f3167274e07 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/comment/comment.module.ts @@ -0,0 +1,50 @@ +import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +import { BaeldungSharedModule } from '../../shared'; + +import { + CommentService, + CommentPopupService, + CommentComponent, + CommentDetailComponent, + CommentDialogComponent, + CommentPopupComponent, + CommentDeletePopupComponent, + CommentDeleteDialogComponent, + commentRoute, + commentPopupRoute, +} from './'; + +let ENTITY_STATES = [ + ...commentRoute, + ...commentPopupRoute, +]; + +@NgModule({ + imports: [ + BaeldungSharedModule, + RouterModule.forRoot(ENTITY_STATES, { useHash: true }) + ], + declarations: [ + CommentComponent, + CommentDetailComponent, + CommentDialogComponent, + CommentDeleteDialogComponent, + CommentPopupComponent, + CommentDeletePopupComponent, + ], + entryComponents: [ + CommentComponent, + CommentDialogComponent, + CommentPopupComponent, + CommentDeleteDialogComponent, + CommentDeletePopupComponent, + ], + providers: [ + CommentService, + CommentPopupService, + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] +}) +export class BaeldungCommentModule {} diff --git a/jhipster/src/main/webapp/app/entities/comment/comment.route.ts b/jhipster/src/main/webapp/app/entities/comment/comment.route.ts new file mode 100644 index 000000000000..bb0e5e414165 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/comment/comment.route.ts @@ -0,0 +1,61 @@ +import { Injectable } from '@angular/core'; +import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot, Routes, CanActivate } from '@angular/router'; + +import { UserRouteAccessService } from '../../shared'; +import { PaginationUtil } from 'ng-jhipster'; + +import { CommentComponent } from './comment.component'; +import { CommentDetailComponent } from './comment-detail.component'; +import { CommentPopupComponent } from './comment-dialog.component'; +import { CommentDeletePopupComponent } from './comment-delete-dialog.component'; + +import { Principal } from '../../shared'; + + +export const commentRoute: Routes = [ + { + path: 'comment', + component: CommentComponent, + data: { + authorities: ['ROLE_USER'], + pageTitle: 'baeldungApp.comment.home.title' + } + }, { + path: 'comment/:id', + component: CommentDetailComponent, + data: { + authorities: ['ROLE_USER'], + pageTitle: 'baeldungApp.comment.home.title' + } + } +]; + +export const commentPopupRoute: Routes = [ + { + path: 'comment-new', + component: CommentPopupComponent, + data: { + authorities: ['ROLE_USER'], + pageTitle: 'baeldungApp.comment.home.title' + }, + outlet: 'popup' + }, + { + path: 'comment/:id/edit', + component: CommentPopupComponent, + data: { + authorities: ['ROLE_USER'], + pageTitle: 'baeldungApp.comment.home.title' + }, + outlet: 'popup' + }, + { + path: 'comment/:id/delete', + component: CommentDeletePopupComponent, + data: { + authorities: ['ROLE_USER'], + pageTitle: 'baeldungApp.comment.home.title' + }, + outlet: 'popup' + } +]; diff --git a/jhipster/src/main/webapp/app/entities/comment/comment.service.ts b/jhipster/src/main/webapp/app/entities/comment/comment.service.ts new file mode 100644 index 000000000000..b6121b3b1033 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/comment/comment.service.ts @@ -0,0 +1,78 @@ +import { Injectable } from '@angular/core'; +import { Http, Response, URLSearchParams, BaseRequestOptions } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +import { Comment } from './comment.model'; +import { DateUtils } from 'ng-jhipster'; +@Injectable() +export class CommentService { + + private resourceUrl = 'api/comments'; + + constructor(private http: Http, private dateUtils: DateUtils) { } + + create(comment: Comment): Observable { + let copy: Comment = Object.assign({}, comment); + copy.creationDate = this.dateUtils + .convertLocalDateToServer(comment.creationDate); + return this.http.post(this.resourceUrl, copy).map((res: Response) => { + return res.json(); + }); + } + + update(comment: Comment): Observable { + let copy: Comment = Object.assign({}, comment); + copy.creationDate = this.dateUtils + .convertLocalDateToServer(comment.creationDate); + return this.http.put(this.resourceUrl, copy).map((res: Response) => { + return res.json(); + }); + } + + find(id: number): Observable { + return this.http.get(`${this.resourceUrl}/${id}`).map((res: Response) => { + let jsonResponse = res.json(); + jsonResponse.creationDate = this.dateUtils + .convertLocalDateFromServer(jsonResponse.creationDate); + return jsonResponse; + }); + } + + query(req?: any): Observable { + let options = this.createRequestOption(req); + return this.http.get(this.resourceUrl, options) + .map((res: any) => this.convertResponse(res)) + ; + } + + delete(id: number): Observable { + return this.http.delete(`${this.resourceUrl}/${id}`); + } + + + private convertResponse(res: any): any { + let jsonResponse = res.json(); + for (let i = 0; i < jsonResponse.length; i++) { + jsonResponse[i].creationDate = this.dateUtils + .convertLocalDateFromServer(jsonResponse[i].creationDate); + } + res._body = jsonResponse; + return res; + } + + private createRequestOption(req?: any): BaseRequestOptions { + let options: BaseRequestOptions = new BaseRequestOptions(); + if (req) { + let params: URLSearchParams = new URLSearchParams(); + params.set('page', req.page); + params.set('size', req.size); + if (req.sort) { + params.paramsMap.set('sort', req.sort); + } + params.set('query', req.query); + + options.search = params; + } + return options; + } +} diff --git a/jhipster/src/main/webapp/app/entities/comment/index.ts b/jhipster/src/main/webapp/app/entities/comment/index.ts new file mode 100644 index 000000000000..5e54fb6099a5 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/comment/index.ts @@ -0,0 +1,8 @@ +export * from './comment.model'; +export * from './comment-popup.service'; +export * from './comment.service'; +export * from './comment-dialog.component'; +export * from './comment-delete-dialog.component'; +export * from './comment-detail.component'; +export * from './comment.component'; +export * from './comment.route'; diff --git a/jhipster/src/main/webapp/app/entities/entity.module.ts b/jhipster/src/main/webapp/app/entities/entity.module.ts new file mode 100644 index 000000000000..ba044e1902fc --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/entity.module.ts @@ -0,0 +1,18 @@ +import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; + +import { BaeldungPostModule } from './post/post.module'; +import { BaeldungCommentModule } from './comment/comment.module'; +/* jhipster-needle-add-entity-module-import - JHipster will add entity modules imports here */ + +@NgModule({ + imports: [ + BaeldungPostModule, + BaeldungCommentModule, + /* jhipster-needle-add-entity-module - JHipster will add entity modules here */ + ], + declarations: [], + entryComponents: [], + providers: [], + schemas: [CUSTOM_ELEMENTS_SCHEMA] +}) +export class BaeldungEntityModule {} diff --git a/jhipster/src/main/webapp/app/entities/post/index.ts b/jhipster/src/main/webapp/app/entities/post/index.ts new file mode 100644 index 000000000000..9375e11bd8bb --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/post/index.ts @@ -0,0 +1,8 @@ +export * from './post.model'; +export * from './post-popup.service'; +export * from './post.service'; +export * from './post-dialog.component'; +export * from './post-delete-dialog.component'; +export * from './post-detail.component'; +export * from './post.component'; +export * from './post.route'; diff --git a/jhipster/src/main/webapp/app/entities/post/post-delete-dialog.component.html b/jhipster/src/main/webapp/app/entities/post/post-delete-dialog.component.html new file mode 100644 index 000000000000..901fc1968eef --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/post/post-delete-dialog.component.html @@ -0,0 +1,19 @@ +
+ + + +
diff --git a/jhipster/src/main/webapp/app/entities/post/post-delete-dialog.component.ts b/jhipster/src/main/webapp/app/entities/post/post-delete-dialog.component.ts new file mode 100644 index 000000000000..5e1413fd95c5 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/post/post-delete-dialog.component.ts @@ -0,0 +1,67 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; + +import { NgbActiveModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { EventManager, JhiLanguageService } from 'ng-jhipster'; + +import { Post } from './post.model'; +import { PostPopupService } from './post-popup.service'; +import { PostService } from './post.service'; + +@Component({ + selector: 'jhi-post-delete-dialog', + templateUrl: './post-delete-dialog.component.html' +}) +export class PostDeleteDialogComponent { + + post: Post; + + constructor( + private jhiLanguageService: JhiLanguageService, + private postService: PostService, + public activeModal: NgbActiveModal, + private eventManager: EventManager + ) { + this.jhiLanguageService.setLocations(['post']); + } + + clear () { + this.activeModal.dismiss('cancel'); + } + + confirmDelete (id: number) { + this.postService.delete(id).subscribe(response => { + this.eventManager.broadcast({ + name: 'postListModification', + content: 'Deleted an post' + }); + this.activeModal.dismiss(true); + }); + } +} + +@Component({ + selector: 'jhi-post-delete-popup', + template: '' +}) +export class PostDeletePopupComponent implements OnInit, OnDestroy { + + modalRef: NgbModalRef; + routeSub: any; + + constructor ( + private route: ActivatedRoute, + private postPopupService: PostPopupService + ) {} + + ngOnInit() { + this.routeSub = this.route.params.subscribe(params => { + this.modalRef = this.postPopupService + .open(PostDeleteDialogComponent, params['id']); + }); + } + + ngOnDestroy() { + this.routeSub.unsubscribe(); + } +} diff --git a/jhipster/src/main/webapp/app/entities/post/post-detail.component.html b/jhipster/src/main/webapp/app/entities/post/post-detail.component.html new file mode 100644 index 000000000000..8edcbee37584 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/post/post-detail.component.html @@ -0,0 +1,37 @@ + +
+

Post {{post.id}}

+
+ +
+
Title
+
+ {{post.title}} +
+
Content
+
+ {{post.content}} +
+
Creation Date
+
+ {{post.creationDate | date:'mediumDate'}} +
+
Creator
+
+ {{post.creator?.login}} +
+
+ + + + +
diff --git a/jhipster/src/main/webapp/app/entities/post/post-detail.component.ts b/jhipster/src/main/webapp/app/entities/post/post-detail.component.ts new file mode 100644 index 000000000000..679592f3b85b --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/post/post-detail.component.ts @@ -0,0 +1,43 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { JhiLanguageService } from 'ng-jhipster'; +import { Post } from './post.model'; +import { PostService } from './post.service'; + +@Component({ + selector: 'jhi-post-detail', + templateUrl: './post-detail.component.html' +}) +export class PostDetailComponent implements OnInit, OnDestroy { + + post: Post; + private subscription: any; + + constructor( + private jhiLanguageService: JhiLanguageService, + private postService: PostService, + private route: ActivatedRoute + ) { + this.jhiLanguageService.setLocations(['post']); + } + + ngOnInit() { + this.subscription = this.route.params.subscribe(params => { + this.load(params['id']); + }); + } + + load (id) { + this.postService.find(id).subscribe(post => { + this.post = post; + }); + } + previousState() { + window.history.back(); + } + + ngOnDestroy() { + this.subscription.unsubscribe(); + } + +} diff --git a/jhipster/src/main/webapp/app/entities/post/post-dialog.component.html b/jhipster/src/main/webapp/app/entities/post/post-dialog.component.html new file mode 100644 index 000000000000..2216dcd451da --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/post/post-dialog.component.html @@ -0,0 +1,96 @@ + + +
+ + + + +
diff --git a/jhipster/src/main/webapp/app/entities/post/post-dialog.component.ts b/jhipster/src/main/webapp/app/entities/post/post-dialog.component.ts new file mode 100644 index 000000000000..803b76fc7839 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/post/post-dialog.component.ts @@ -0,0 +1,107 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { Response } from '@angular/http'; + +import { NgbActiveModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { EventManager, AlertService, JhiLanguageService } from 'ng-jhipster'; + +import { Post } from './post.model'; +import { PostPopupService } from './post-popup.service'; +import { PostService } from './post.service'; +import { User, UserService } from '../../shared'; +@Component({ + selector: 'jhi-post-dialog', + templateUrl: './post-dialog.component.html' +}) +export class PostDialogComponent implements OnInit { + + post: Post; + authorities: any[]; + isSaving: boolean; + + users: User[]; + constructor( + public activeModal: NgbActiveModal, + private jhiLanguageService: JhiLanguageService, + private alertService: AlertService, + private postService: PostService, + private userService: UserService, + private eventManager: EventManager + ) { + this.jhiLanguageService.setLocations(['post']); + } + + ngOnInit() { + this.isSaving = false; + this.authorities = ['ROLE_USER', 'ROLE_ADMIN']; + this.userService.query().subscribe( + (res: Response) => { this.users = res.json(); }, (res: Response) => this.onError(res.json())); + } + clear () { + this.activeModal.dismiss('cancel'); + } + + save () { + this.isSaving = true; + if (this.post.id !== undefined) { + this.postService.update(this.post) + .subscribe((res: Post) => + this.onSaveSuccess(res), (res: Response) => this.onSaveError(res.json())); + } else { + this.postService.create(this.post) + .subscribe((res: Post) => + this.onSaveSuccess(res), (res: Response) => this.onSaveError(res.json())); + } + } + + private onSaveSuccess (result: Post) { + this.eventManager.broadcast({ name: 'postListModification', content: 'OK'}); + this.isSaving = false; + this.activeModal.dismiss(result); + } + + private onSaveError (error) { + this.isSaving = false; + this.onError(error); + } + + private onError (error) { + this.alertService.error(error.message, null, null); + } + + trackUserById(index: number, item: User) { + return item.id; + } +} + +@Component({ + selector: 'jhi-post-popup', + template: '' +}) +export class PostPopupComponent implements OnInit, OnDestroy { + + modalRef: NgbModalRef; + routeSub: any; + + constructor ( + private route: ActivatedRoute, + private postPopupService: PostPopupService + ) {} + + ngOnInit() { + this.routeSub = this.route.params.subscribe(params => { + if ( params['id'] ) { + this.modalRef = this.postPopupService + .open(PostDialogComponent, params['id']); + } else { + this.modalRef = this.postPopupService + .open(PostDialogComponent); + } + + }); + } + + ngOnDestroy() { + this.routeSub.unsubscribe(); + } +} diff --git a/jhipster/src/main/webapp/app/entities/post/post-popup.service.ts b/jhipster/src/main/webapp/app/entities/post/post-popup.service.ts new file mode 100644 index 000000000000..42ea731e07d6 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/post/post-popup.service.ts @@ -0,0 +1,50 @@ +import { Injectable, Component } from '@angular/core'; +import { Router } from '@angular/router'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { Post } from './post.model'; +import { PostService } from './post.service'; +@Injectable() +export class PostPopupService { + private isOpen = false; + constructor ( + private modalService: NgbModal, + private router: Router, + private postService: PostService + + ) {} + + open (component: Component, id?: number | any): NgbModalRef { + if (this.isOpen) { + return; + } + this.isOpen = true; + + if (id) { + this.postService.find(id).subscribe(post => { + if (post.creationDate) { + post.creationDate = { + year: post.creationDate.getFullYear(), + month: post.creationDate.getMonth() + 1, + day: post.creationDate.getDate() + }; + } + this.postModalRef(component, post); + }); + } else { + return this.postModalRef(component, new Post()); + } + } + + postModalRef(component: Component, post: Post): NgbModalRef { + let modalRef = this.modalService.open(component, { size: 'lg', backdrop: 'static'}); + modalRef.componentInstance.post = post; + modalRef.result.then(result => { + this.router.navigate([{ outlets: { popup: null }}], { replaceUrl: true }); + this.isOpen = false; + }, (reason) => { + this.router.navigate([{ outlets: { popup: null }}], { replaceUrl: true }); + this.isOpen = false; + }); + return modalRef; + } +} diff --git a/jhipster/src/main/webapp/app/entities/post/post.component.html b/jhipster/src/main/webapp/app/entities/post/post.component.html new file mode 100644 index 000000000000..cf17c4e6dc40 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/post/post.component.html @@ -0,0 +1,64 @@ +
+

+ Posts + +

+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + +
ID Title Content Creation Date Creator
{{post.id}}{{post.title}}{{post.content}}{{post.creationDate | date:'mediumDate'}} + {{post.creator?.login}} + +
+ + + +
+
+
+
diff --git a/jhipster/src/main/webapp/app/entities/post/post.component.ts b/jhipster/src/main/webapp/app/entities/post/post.component.ts new file mode 100644 index 000000000000..e8401a48c716 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/post/post.component.ts @@ -0,0 +1,110 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Response } from '@angular/http'; +import { ActivatedRoute, Router } from '@angular/router'; +import { Subscription } from 'rxjs/Rx'; +import { EventManager, ParseLinks, PaginationUtil, JhiLanguageService, AlertService } from 'ng-jhipster'; + +import { Post } from './post.model'; +import { PostService } from './post.service'; +import { ITEMS_PER_PAGE, Principal } from '../../shared'; +import { PaginationConfig } from '../../blocks/config/uib-pagination.config'; + +@Component({ + selector: 'jhi-post', + templateUrl: './post.component.html' +}) +export class PostComponent implements OnInit, OnDestroy { + + posts: Post[]; + currentAccount: any; + eventSubscriber: Subscription; + itemsPerPage: number; + links: any; + page: any; + predicate: any; + queryCount: any; + reverse: any; + totalItems: number; + + constructor( + private jhiLanguageService: JhiLanguageService, + private postService: PostService, + private alertService: AlertService, + private eventManager: EventManager, + private parseLinks: ParseLinks, + private principal: Principal + ) { + this.posts = []; + this.itemsPerPage = ITEMS_PER_PAGE; + this.page = 0; + this.links = { + last: 0 + }; + this.predicate = 'id'; + this.reverse = true; + this.jhiLanguageService.setLocations(['post']); + } + + loadAll () { + this.postService.query({ + page: this.page, + size: this.itemsPerPage, + sort: this.sort() + }).subscribe( + (res: Response) => this.onSuccess(res.json(), res.headers), + (res: Response) => this.onError(res.json()) + ); + } + + reset () { + this.page = 0; + this.posts = []; + this.loadAll(); + } + + loadPage(page) { + this.page = page; + this.loadAll(); + } + ngOnInit() { + this.loadAll(); + this.principal.identity().then((account) => { + this.currentAccount = account; + }); + this.registerChangeInPosts(); + } + + ngOnDestroy() { + this.eventManager.destroy(this.eventSubscriber); + } + + trackId (index: number, item: Post) { + return item.id; + } + + + + registerChangeInPosts() { + this.eventSubscriber = this.eventManager.subscribe('postListModification', (response) => this.reset()); + } + + sort () { + let result = [this.predicate + ',' + (this.reverse ? 'asc' : 'desc')]; + if (this.predicate !== 'id') { + result.push('id'); + } + return result; + } + + private onSuccess(data, headers) { + this.links = this.parseLinks.parse(headers.get('link')); + this.totalItems = headers.get('X-Total-Count'); + for (let i = 0; i < data.length; i++) { + this.posts.push(data[i]); + } + } + + private onError (error) { + this.alertService.error(error.message, null, null); + } +} diff --git a/jhipster/src/main/webapp/app/entities/post/post.model.ts b/jhipster/src/main/webapp/app/entities/post/post.model.ts new file mode 100644 index 000000000000..454f7e75f8b2 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/post/post.model.ts @@ -0,0 +1,11 @@ +import { User } from '../../shared'; +export class Post { + constructor( + public id?: number, + public title?: string, + public content?: string, + public creationDate?: any, + public creator?: User, + ) { + } +} diff --git a/jhipster/src/main/webapp/app/entities/post/post.module.ts b/jhipster/src/main/webapp/app/entities/post/post.module.ts new file mode 100644 index 000000000000..53ba982c021a --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/post/post.module.ts @@ -0,0 +1,52 @@ +import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +import { BaeldungSharedModule } from '../../shared'; +import { BaeldungAdminModule } from '../../admin/admin.module'; + +import { + PostService, + PostPopupService, + PostComponent, + PostDetailComponent, + PostDialogComponent, + PostPopupComponent, + PostDeletePopupComponent, + PostDeleteDialogComponent, + postRoute, + postPopupRoute, +} from './'; + +let ENTITY_STATES = [ + ...postRoute, + ...postPopupRoute, +]; + +@NgModule({ + imports: [ + BaeldungSharedModule, + BaeldungAdminModule, + RouterModule.forRoot(ENTITY_STATES, { useHash: true }) + ], + declarations: [ + PostComponent, + PostDetailComponent, + PostDialogComponent, + PostDeleteDialogComponent, + PostPopupComponent, + PostDeletePopupComponent, + ], + entryComponents: [ + PostComponent, + PostDialogComponent, + PostPopupComponent, + PostDeleteDialogComponent, + PostDeletePopupComponent, + ], + providers: [ + PostService, + PostPopupService, + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] +}) +export class BaeldungPostModule {} diff --git a/jhipster/src/main/webapp/app/entities/post/post.route.ts b/jhipster/src/main/webapp/app/entities/post/post.route.ts new file mode 100644 index 000000000000..27b23bdc0d21 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/post/post.route.ts @@ -0,0 +1,61 @@ +import { Injectable } from '@angular/core'; +import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot, Routes, CanActivate } from '@angular/router'; + +import { UserRouteAccessService } from '../../shared'; +import { PaginationUtil } from 'ng-jhipster'; + +import { PostComponent } from './post.component'; +import { PostDetailComponent } from './post-detail.component'; +import { PostPopupComponent } from './post-dialog.component'; +import { PostDeletePopupComponent } from './post-delete-dialog.component'; + +import { Principal } from '../../shared'; + + +export const postRoute: Routes = [ + { + path: 'post', + component: PostComponent, + data: { + authorities: ['ROLE_USER'], + pageTitle: 'baeldungApp.post.home.title' + } + }, { + path: 'post/:id', + component: PostDetailComponent, + data: { + authorities: ['ROLE_USER'], + pageTitle: 'baeldungApp.post.home.title' + } + } +]; + +export const postPopupRoute: Routes = [ + { + path: 'post-new', + component: PostPopupComponent, + data: { + authorities: ['ROLE_USER'], + pageTitle: 'baeldungApp.post.home.title' + }, + outlet: 'popup' + }, + { + path: 'post/:id/edit', + component: PostPopupComponent, + data: { + authorities: ['ROLE_USER'], + pageTitle: 'baeldungApp.post.home.title' + }, + outlet: 'popup' + }, + { + path: 'post/:id/delete', + component: PostDeletePopupComponent, + data: { + authorities: ['ROLE_USER'], + pageTitle: 'baeldungApp.post.home.title' + }, + outlet: 'popup' + } +]; diff --git a/jhipster/src/main/webapp/app/entities/post/post.service.ts b/jhipster/src/main/webapp/app/entities/post/post.service.ts new file mode 100644 index 000000000000..7f7ee969febc --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/post/post.service.ts @@ -0,0 +1,78 @@ +import { Injectable } from '@angular/core'; +import { Http, Response, URLSearchParams, BaseRequestOptions } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +import { Post } from './post.model'; +import { DateUtils } from 'ng-jhipster'; +@Injectable() +export class PostService { + + private resourceUrl = 'api/posts'; + + constructor(private http: Http, private dateUtils: DateUtils) { } + + create(post: Post): Observable { + let copy: Post = Object.assign({}, post); + copy.creationDate = this.dateUtils + .convertLocalDateToServer(post.creationDate); + return this.http.post(this.resourceUrl, copy).map((res: Response) => { + return res.json(); + }); + } + + update(post: Post): Observable { + let copy: Post = Object.assign({}, post); + copy.creationDate = this.dateUtils + .convertLocalDateToServer(post.creationDate); + return this.http.put(this.resourceUrl, copy).map((res: Response) => { + return res.json(); + }); + } + + find(id: number): Observable { + return this.http.get(`${this.resourceUrl}/${id}`).map((res: Response) => { + let jsonResponse = res.json(); + jsonResponse.creationDate = this.dateUtils + .convertLocalDateFromServer(jsonResponse.creationDate); + return jsonResponse; + }); + } + + query(req?: any): Observable { + let options = this.createRequestOption(req); + return this.http.get(this.resourceUrl, options) + .map((res: any) => this.convertResponse(res)) + ; + } + + delete(id: number): Observable { + return this.http.delete(`${this.resourceUrl}/${id}`); + } + + + private convertResponse(res: any): any { + let jsonResponse = res.json(); + for (let i = 0; i < jsonResponse.length; i++) { + jsonResponse[i].creationDate = this.dateUtils + .convertLocalDateFromServer(jsonResponse[i].creationDate); + } + res._body = jsonResponse; + return res; + } + + private createRequestOption(req?: any): BaseRequestOptions { + let options: BaseRequestOptions = new BaseRequestOptions(); + if (req) { + let params: URLSearchParams = new URLSearchParams(); + params.set('page', req.page); + params.set('size', req.size); + if (req.sort) { + params.paramsMap.set('sort', req.sort); + } + params.set('query', req.query); + + options.search = params; + } + return options; + } +} diff --git a/jhipster/src/main/webapp/app/home/home.component.html b/jhipster/src/main/webapp/app/home/home.component.html new file mode 100644 index 000000000000..4c0197228c00 --- /dev/null +++ b/jhipster/src/main/webapp/app/home/home.component.html @@ -0,0 +1,42 @@ +
+
+ +
+
+

Welcome, Java Hipster!

+

This is your homepage

+ +
+
+ You are logged in as user "{{account.login}}". +
+ +
+ If you want to + sign in, you can try the default accounts:
- Administrator (login="admin" and password="admin")
- User (login="user" and password="user").
+
+ +
+ You don't have an account yet? + Register a new account +
+
+ +

+ If you have any question on JHipster: +

+ + + +

+ If you like JHipster, don't forget to give us a star on Github! +

+
+
diff --git a/jhipster/src/main/webapp/app/home/home.component.ts b/jhipster/src/main/webapp/app/home/home.component.ts new file mode 100644 index 000000000000..b16838377e29 --- /dev/null +++ b/jhipster/src/main/webapp/app/home/home.component.ts @@ -0,0 +1,50 @@ +import { Component, OnInit } from '@angular/core'; +import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { EventManager, JhiLanguageService } from 'ng-jhipster'; + +import { Account, LoginModalService, Principal } from '../shared'; + +@Component({ + selector: 'jhi-home', + templateUrl: './home.component.html', + styleUrls: [ + 'home.scss' + ] + +}) +export class HomeComponent implements OnInit { + account: Account; + modalRef: NgbModalRef; + + constructor( + private jhiLanguageService: JhiLanguageService, + private principal: Principal, + private loginModalService: LoginModalService, + private eventManager: EventManager + ) { + this.jhiLanguageService.setLocations(['home']); + } + + ngOnInit() { + this.principal.identity().then((account) => { + this.account = account; + }); + this.registerAuthenticationSuccess(); + } + + registerAuthenticationSuccess() { + this.eventManager.subscribe('authenticationSuccess', (message) => { + this.principal.identity().then((account) => { + this.account = account; + }); + }); + } + + isAuthenticated() { + return this.principal.isAuthenticated(); + } + + login() { + this.modalRef = this.loginModalService.open(); + } +} diff --git a/jhipster/src/main/webapp/app/home/home.module.ts b/jhipster/src/main/webapp/app/home/home.module.ts new file mode 100644 index 000000000000..172c605249b9 --- /dev/null +++ b/jhipster/src/main/webapp/app/home/home.module.ts @@ -0,0 +1,23 @@ +import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +import { BaeldungSharedModule } from '../shared'; + +import { HOME_ROUTE, HomeComponent } from './'; + + +@NgModule({ + imports: [ + BaeldungSharedModule, + RouterModule.forRoot([ HOME_ROUTE ], { useHash: true }) + ], + declarations: [ + HomeComponent, + ], + entryComponents: [ + ], + providers: [ + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] +}) +export class BaeldungHomeModule {} diff --git a/jhipster/src/main/webapp/app/home/home.route.ts b/jhipster/src/main/webapp/app/home/home.route.ts new file mode 100644 index 000000000000..cba14bf0c65e --- /dev/null +++ b/jhipster/src/main/webapp/app/home/home.route.ts @@ -0,0 +1,14 @@ +import { Route } from '@angular/router'; + +import { UserRouteAccessService } from '../shared'; +import { HomeComponent } from './'; + +export const HOME_ROUTE: Route = { + path: '', + component: HomeComponent, + data: { + authorities: [], + pageTitle: 'home.title' + }, + canActivate: [UserRouteAccessService] +}; diff --git a/jhipster/src/main/webapp/app/home/home.scss b/jhipster/src/main/webapp/app/home/home.scss new file mode 100644 index 000000000000..578787b1c9f7 --- /dev/null +++ b/jhipster/src/main/webapp/app/home/home.scss @@ -0,0 +1,26 @@ + +/* ========================================================================== +Main page styles +========================================================================== */ + +.hipster { + display: inline-block; + width: 347px; + height: 497px; + background: url("../../content/images/hipster.png") no-repeat center top; + background-size: contain; +} + +/* wait autoprefixer update to allow simple generation of high pixel density media query */ +@media +only screen and (-webkit-min-device-pixel-ratio: 2), +only screen and ( min--moz-device-pixel-ratio: 2), +only screen and ( -o-min-device-pixel-ratio: 2/1), +only screen and ( min-device-pixel-ratio: 2), +only screen and ( min-resolution: 192dpi), +only screen and ( min-resolution: 2dppx) { + .hipster { + background: url("../../content/images/hipster2x.png") no-repeat center top; + background-size: contain; + } +} diff --git a/jhipster/src/main/webapp/app/home/index.ts b/jhipster/src/main/webapp/app/home/index.ts new file mode 100644 index 000000000000..d76285b27776 --- /dev/null +++ b/jhipster/src/main/webapp/app/home/index.ts @@ -0,0 +1,3 @@ +export * from './home.component'; +export * from './home.route'; +export * from './home.module'; diff --git a/jhipster/src/main/webapp/app/layouts/error/error.component.html b/jhipster/src/main/webapp/app/layouts/error/error.component.html new file mode 100644 index 000000000000..92fe2f151258 --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/error/error.component.html @@ -0,0 +1,17 @@ +
+
+
+ +
+
+

Error Page!

+ +
+
{{errorMessage}} +
+
+
You are not authorized to access the page. +
+
+
+
diff --git a/jhipster/src/main/webapp/app/layouts/error/error.component.ts b/jhipster/src/main/webapp/app/layouts/error/error.component.ts new file mode 100644 index 000000000000..983809f5410a --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/error/error.component.ts @@ -0,0 +1,20 @@ +import { Component, OnInit } from '@angular/core'; +import { JhiLanguageService } from 'ng-jhipster'; + +@Component({ + selector: 'jhi-error', + templateUrl: './error.component.html' +}) +export class ErrorComponent implements OnInit { + errorMessage: string; + error403: boolean; + + constructor( + private jhiLanguageService: JhiLanguageService + ) { + this.jhiLanguageService.setLocations(['error']); + } + + ngOnInit() { + } +} diff --git a/jhipster/src/main/webapp/app/layouts/error/error.route.ts b/jhipster/src/main/webapp/app/layouts/error/error.route.ts new file mode 100644 index 000000000000..5f6085076c1f --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/error/error.route.ts @@ -0,0 +1,25 @@ +import { Routes } from '@angular/router'; + +import { UserRouteAccessService } from '../../shared'; +import { ErrorComponent } from './error.component'; + +export const errorRoute: Routes = [ + { + path: 'error', + component: ErrorComponent, + data: { + authorities: [], + pageTitle: 'error.title' + }, + canActivate: [UserRouteAccessService] + }, + { + path: 'accessdenied', + component: ErrorComponent, + data: { + authorities: [], + pageTitle: 'error.title' + }, + canActivate: [UserRouteAccessService] + } +]; diff --git a/jhipster/src/main/webapp/app/layouts/footer/footer.component.html b/jhipster/src/main/webapp/app/layouts/footer/footer.component.html new file mode 100644 index 000000000000..4e4fda05bf56 --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/footer/footer.component.html @@ -0,0 +1,4 @@ + + diff --git a/jhipster/src/main/webapp/app/layouts/footer/footer.component.ts b/jhipster/src/main/webapp/app/layouts/footer/footer.component.ts new file mode 100644 index 000000000000..37da8bca7544 --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/footer/footer.component.ts @@ -0,0 +1,7 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'jhi-footer', + templateUrl: './footer.component.html' +}) +export class FooterComponent {} diff --git a/jhipster/src/main/webapp/app/layouts/index.ts b/jhipster/src/main/webapp/app/layouts/index.ts new file mode 100644 index 000000000000..f25305a0ac06 --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/index.ts @@ -0,0 +1,10 @@ +export * from './error/error.component'; +export * from './error/error.route'; +export * from './main/main.component'; +export * from './footer/footer.component'; +export * from './navbar/navbar.component'; +export * from './navbar/active-menu.directive'; +export * from './profiles/page-ribbon.component'; +export * from './profiles/profile.service'; +export * from './profiles/profile-info.model'; +export * from './layout-routing.module'; diff --git a/jhipster/src/main/webapp/app/layouts/layout-routing.module.ts b/jhipster/src/main/webapp/app/layouts/layout-routing.module.ts new file mode 100644 index 000000000000..8edbdff26cc3 --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/layout-routing.module.ts @@ -0,0 +1,20 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes, Resolve } from '@angular/router'; + +import { navbarRoute } from '../app.route'; +import { errorRoute } from './'; + +let LAYOUT_ROUTES = [ + navbarRoute, + ...errorRoute +]; + +@NgModule({ + imports: [ + RouterModule.forRoot(LAYOUT_ROUTES, { useHash: true }) + ], + exports: [ + RouterModule + ] +}) +export class LayoutRoutingModule {} diff --git a/jhipster/src/main/webapp/app/layouts/main/main.component.html b/jhipster/src/main/webapp/app/layouts/main/main.component.html new file mode 100644 index 000000000000..5bcd12ab0b84 --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/main/main.component.html @@ -0,0 +1,11 @@ + +
+ +
+
+
+ + +
+ +
diff --git a/jhipster/src/main/webapp/app/layouts/main/main.component.ts b/jhipster/src/main/webapp/app/layouts/main/main.component.ts new file mode 100644 index 000000000000..c5982f60caa5 --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/main/main.component.ts @@ -0,0 +1,47 @@ +import { Component, OnInit } from '@angular/core'; +import { Router, ActivatedRouteSnapshot, NavigationEnd, RoutesRecognized } from '@angular/router'; + +import { JhiLanguageHelper, StateStorageService } from '../../shared'; + +@Component({ + selector: 'jhi-main', + templateUrl: './main.component.html' +}) +export class JhiMainComponent implements OnInit { + + constructor( + private jhiLanguageHelper: JhiLanguageHelper, + private router: Router, + private $storageService: StateStorageService, + ) {} + + private getPageTitle(routeSnapshot: ActivatedRouteSnapshot) { + let title: string = (routeSnapshot.data && routeSnapshot.data['pageTitle']) ? routeSnapshot.data['pageTitle'] : 'baeldungApp'; + if (routeSnapshot.firstChild) { + title = this.getPageTitle(routeSnapshot.firstChild) || title; + } + return title; + } + + ngOnInit() { + this.router.events.subscribe((event) => { + if (event instanceof NavigationEnd) { + this.jhiLanguageHelper.updateTitle(this.getPageTitle(this.router.routerState.snapshot.root)); + } + if (event instanceof RoutesRecognized) { + let params = {}; + let destinationData = {}; + let destinationName = ''; + let destinationEvent = event.state.root.firstChild.children[0]; + if (destinationEvent !== undefined) { + params = destinationEvent.params; + destinationData = destinationEvent.data; + destinationName = destinationEvent.url[0].path; + } + let from = {name: this.router.url.slice(1)}; + let destination = {name: destinationName, data: destinationData}; + this.$storageService.storeDestinationState(destination, params, from); + } + }); + } +} diff --git a/jhipster/src/main/webapp/app/layouts/navbar/active-menu.directive.ts b/jhipster/src/main/webapp/app/layouts/navbar/active-menu.directive.ts new file mode 100644 index 000000000000..87275c9d57a9 --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/navbar/active-menu.directive.ts @@ -0,0 +1,26 @@ +import { Directive, OnInit, ElementRef, Renderer, Input} from '@angular/core'; +import { TranslateService, LangChangeEvent } from 'ng2-translate'; + +@Directive({ + selector: '[jhiActiveMenu]' +}) +export class ActiveMenuDirective implements OnInit { + @Input() jhiActiveMenu: string; + + constructor(private el: ElementRef, private renderer: Renderer, private translateService: TranslateService) {} + + ngOnInit() { + this.translateService.onLangChange.subscribe((event: LangChangeEvent) => { + this.updateActiveFlag(event.lang); + }); + this.updateActiveFlag(this.translateService.currentLang); + } + + updateActiveFlag(selectedLanguage) { + if (this.jhiActiveMenu === selectedLanguage) { + this.renderer.setElementClass(this.el.nativeElement, 'active', true); + } else { + this.renderer.setElementClass(this.el.nativeElement, 'active', false); + } + } +} diff --git a/jhipster/src/main/webapp/app/layouts/navbar/navbar.component.html b/jhipster/src/main/webapp/app/layouts/navbar/navbar.component.html new file mode 100644 index 000000000000..07b7abb25c22 --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/navbar/navbar.component.html @@ -0,0 +1,168 @@ + diff --git a/jhipster/src/main/webapp/app/layouts/navbar/navbar.component.ts b/jhipster/src/main/webapp/app/layouts/navbar/navbar.component.ts new file mode 100644 index 000000000000..8f58bfebd9db --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/navbar/navbar.component.ts @@ -0,0 +1,81 @@ +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { JhiLanguageService } from 'ng-jhipster'; + +import { ProfileService } from '../profiles/profile.service'; // FIXME barrel doesnt work here +import { JhiLanguageHelper, Principal, LoginModalService, LoginService } from '../../shared'; + +import { VERSION, DEBUG_INFO_ENABLED } from '../../app.constants'; + +@Component({ + selector: 'jhi-navbar', + templateUrl: './navbar.component.html', + styleUrls: [ + 'navbar.scss' + ] +}) +export class NavbarComponent implements OnInit { + + inProduction: boolean; + isNavbarCollapsed: boolean; + languages: any[]; + swaggerEnabled: boolean; + modalRef: NgbModalRef; + version: string; + + constructor( + private loginService: LoginService, + private languageHelper: JhiLanguageHelper, + private languageService: JhiLanguageService, + private principal: Principal, + private loginModalService: LoginModalService, + private profileService: ProfileService, + private router: Router + ) { + this.version = DEBUG_INFO_ENABLED ? 'v' + VERSION : ''; + this.isNavbarCollapsed = true; + this.languageService.addLocation('home'); + } + + ngOnInit() { + this.languageHelper.getAll().then((languages) => { + this.languages = languages; + }); + + this.profileService.getProfileInfo().subscribe(profileInfo => { + this.inProduction = profileInfo.inProduction; + this.swaggerEnabled = profileInfo.swaggerEnabled; + }); + } + + changeLanguage(languageKey: string) { + this.languageService.changeLanguage(languageKey); + } + + collapseNavbar() { + this.isNavbarCollapsed = true; + } + + isAuthenticated() { + return this.principal.isAuthenticated(); + } + + login() { + this.modalRef = this.loginModalService.open(); + } + + logout() { + this.collapseNavbar(); + this.loginService.logout(); + this.router.navigate(['']); + } + + toggleNavbar() { + this.isNavbarCollapsed = !this.isNavbarCollapsed; + } + + getImageUrl() { + return this.isAuthenticated() ? this.principal.getImageUrl() : null; + } +} diff --git a/jhipster/src/main/webapp/app/layouts/navbar/navbar.scss b/jhipster/src/main/webapp/app/layouts/navbar/navbar.scss new file mode 100644 index 000000000000..d48d60954388 --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/navbar/navbar.scss @@ -0,0 +1,70 @@ + +/* ========================================================================== +Navbar +========================================================================== */ +.navbar-version { + font-size: 10px; + color: #ccc +} + +.jh-navbar { + background-color: #353d47; + padding: .2em 1em; + .profile-image { + margin: -10px 0px; + height: 40px; + width: 40px; + border-radius: 50%; + } + .dropdown-item.active, .dropdown-item.active:focus, .dropdown-item.active:hover { + background-color: #353d47; + } + .dropdown-toggle::after { + margin-left: 0.15em; + } + ul.navbar-nav { + padding: 0.5em; + .nav-item { + margin-left: 1.5rem; + } + } + a.nav-link { + font-weight: 400; + } + .jh-navbar-toggler { + color: #ccc; + font-size: 1.5em; + padding: 10px; + &:hover { + color: #fff; + } + } +} + +@media screen and (max-width: 992px) { + .jh-logo-container { + width: 100%; + } +} + +.navbar-title { + display: inline-block; + vertical-align: middle; +} + +/* ========================================================================== +Logo styles +========================================================================== */ +.navbar-brand { + &.logo { + padding: 5px 15px; + .logo-img { + height: 45px; + width: 70px; + display: inline-block; + vertical-align: middle; + background: url("../../../content/images/logo-jhipster.png") no-repeat center center; + background-size: contain; + } + } +} diff --git a/jhipster/src/main/webapp/app/layouts/profiles/page-ribbon.component.ts b/jhipster/src/main/webapp/app/layouts/profiles/page-ribbon.component.ts new file mode 100644 index 000000000000..f7ba492f6637 --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/profiles/page-ribbon.component.ts @@ -0,0 +1,25 @@ +import { Component, OnInit } from '@angular/core'; +import { ProfileService } from './profile.service'; +import { ProfileInfo } from './profile-info.model'; + +@Component({ + selector: 'jhi-page-ribbon', + template: ``, + styleUrls: [ + 'page-ribbon.scss' + ] +}) +export class PageRibbonComponent implements OnInit { + + profileInfo: ProfileInfo; + ribbonEnv: string; + + constructor(private profileService: ProfileService) {} + + ngOnInit() { + this.profileService.getProfileInfo().subscribe(profileInfo => { + this.profileInfo = profileInfo; + this.ribbonEnv = profileInfo.ribbonEnv; + }); + } +} diff --git a/jhipster/src/main/webapp/app/layouts/profiles/page-ribbon.scss b/jhipster/src/main/webapp/app/layouts/profiles/page-ribbon.scss new file mode 100644 index 000000000000..5efd11c03ea4 --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/profiles/page-ribbon.scss @@ -0,0 +1,32 @@ + +/* ========================================================================== +Developement Ribbon +========================================================================== */ +.ribbon { + background-color: rgba(170, 0, 0, 0.5); + left: -3.5em; + moz-transform: rotate(-45deg); + ms-transform: rotate(-45deg); + o-transform: rotate(-45deg); + webkit-transform: rotate(-45deg); + transform: rotate(-45deg); + overflow: hidden; + position: absolute; + top: 40px; + white-space: nowrap; + width: 15em; + z-index: 9999; + pointer-events: none; + opacity: 0.75; + a { + color: #fff; + display: block; + font-weight: 400; + margin: 1px 0; + padding: 10px 50px; + text-align: center; + text-decoration: none; + text-shadow: 0 0 5px #444; + pointer-events: none; + } +} diff --git a/jhipster/src/main/webapp/app/layouts/profiles/profile-info.model.ts b/jhipster/src/main/webapp/app/layouts/profiles/profile-info.model.ts new file mode 100644 index 000000000000..f1adc52c7bf2 --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/profiles/profile-info.model.ts @@ -0,0 +1,6 @@ +export class ProfileInfo { + activeProfiles: string[]; + ribbonEnv: string; + inProduction: boolean; + swaggerEnabled: boolean; +} diff --git a/jhipster/src/main/webapp/app/layouts/profiles/profile.service.ts b/jhipster/src/main/webapp/app/layouts/profiles/profile.service.ts new file mode 100644 index 000000000000..347b84d8e521 --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/profiles/profile.service.ts @@ -0,0 +1,26 @@ +import { Injectable } from '@angular/core'; +import { Http, Response } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +import { ProfileInfo } from './profile-info.model'; + +@Injectable() +export class ProfileService { + + private profileInfoUrl = 'api/profile-info'; + + constructor(private http: Http) { } + + getProfileInfo(): Observable { + return this.http.get(this.profileInfoUrl) + .map((res: Response) => { + let data = res.json(); + let pi = new ProfileInfo(); + pi.activeProfiles = data.activeProfiles; + pi.ribbonEnv = data.ribbonEnv; + pi.inProduction = data.activeProfiles.indexOf('prod') !== -1; + pi.swaggerEnabled = data.activeProfiles.indexOf('swagger') !== -1; + return pi; + }); + } +} diff --git a/jhipster/src/main/webapp/app/polyfills.ts b/jhipster/src/main/webapp/app/polyfills.ts new file mode 100644 index 000000000000..0771ba0b72ec --- /dev/null +++ b/jhipster/src/main/webapp/app/polyfills.ts @@ -0,0 +1,3 @@ +/* tslint:disable */ +import 'reflect-metadata/Reflect'; +import 'zone.js/dist/zone'; diff --git a/jhipster/src/main/webapp/app/shared/alert/alert-error.component.ts b/jhipster/src/main/webapp/app/shared/alert/alert-error.component.ts new file mode 100644 index 000000000000..e9e3e2b7a021 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/alert/alert-error.component.ts @@ -0,0 +1,101 @@ +import { Component, OnDestroy } from '@angular/core'; +import { TranslateService } from 'ng2-translate'; +import { EventManager, AlertService } from 'ng-jhipster'; +import { Subscription } from 'rxjs/Rx'; + +@Component({ + selector: 'jhi-alert-error', + template: ` + ` +}) +export class JhiAlertErrorComponent implements OnDestroy { + + alerts: any[]; + cleanHttpErrorListener: Subscription; + + constructor(private alertService: AlertService, private eventManager: EventManager, private translateService: TranslateService) { + this.alerts = []; + + this.cleanHttpErrorListener = eventManager.subscribe('baeldungApp.httpError', (response) => { + let i; + let httpResponse = response.content; + switch (httpResponse.status) { + // connection refused, server not reachable + case 0: + this.addErrorAlert('Server not reachable', 'error.server.not.reachable'); + break; + + case 400: + let arr = Array.from(httpResponse.headers._headers); + let headers = []; + for (i = 0; i < arr.length; i++) { + if (arr[i][0].endsWith('app-error') || arr[i][0].endsWith('app-params')) { + headers.push(arr[i][0]); + } + } + headers.sort(); + let errorHeader = httpResponse.headers.get(headers[0]); + let entityKey = httpResponse.headers.get(headers[1]); + if (errorHeader) { + let entityName = translateService.instant('global.menu.entities.' + entityKey); + this.addErrorAlert(errorHeader, errorHeader, {entityName: entityName}); + } else if (httpResponse.text() !== '' && httpResponse.json() && httpResponse.json().fieldErrors) { + let fieldErrors = httpResponse.json().fieldErrors; + for (i = 0; i < fieldErrors.length; i++) { + let fieldError = fieldErrors[i]; + // convert 'something[14].other[4].id' to 'something[].other[].id' so translations can be written to it + let convertedField = fieldError.field.replace(/\[\d*\]/g, '[]'); + let fieldName = translateService.instant('baeldungApp.' + + fieldError.objectName + '.' + convertedField); + this.addErrorAlert( + 'Field ' + fieldName + ' cannot be empty', 'error.' + fieldError.message, {fieldName: fieldName}); + } + } else if (httpResponse.text() !== '' && httpResponse.json() && httpResponse.json().message) { + this.addErrorAlert(httpResponse.json().message, httpResponse.json().message, httpResponse.json()); + } else { + this.addErrorAlert(httpResponse.text()); + } + break; + + case 404: + this.addErrorAlert('Not found', 'error.url.not.found'); + break; + + default: + if (httpResponse.text() !== '' && httpResponse.json() && httpResponse.json().message) { + this.addErrorAlert(httpResponse.json().message); + } else { + this.addErrorAlert(JSON.stringify(httpResponse)); // Fixme find a way to parse httpResponse + } + } + }); + } + + ngOnDestroy() { + if (this.cleanHttpErrorListener !== undefined && this.cleanHttpErrorListener !== null) { + this.eventManager.destroy(this.cleanHttpErrorListener); + this.alerts = []; + } + } + + addErrorAlert (message, key?, data?) { + key = key && key !== null ? key : message; + this.alerts.push( + this.alertService.addAlert( + { + type: 'danger', + msg: key, + params: data, + timeout: 5000, + toast: this.alertService.isToast(), + scoped: true + }, + this.alerts + ) + ); + } +} diff --git a/jhipster/src/main/webapp/app/shared/alert/alert.component.ts b/jhipster/src/main/webapp/app/shared/alert/alert.component.ts new file mode 100644 index 000000000000..b8aa418ac581 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/alert/alert.component.ts @@ -0,0 +1,26 @@ +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { AlertService } from 'ng-jhipster'; + +@Component({ + selector: 'jhi-alert', + template: ` + ` +}) +export class JhiAlertComponent implements OnInit, OnDestroy { + alerts: any[]; + + constructor(private alertService: AlertService) { } + + ngOnInit() { + this.alerts = this.alertService.get(); + } + + ngOnDestroy() { + this.alerts = []; + } + +} diff --git a/jhipster/src/main/webapp/app/shared/auth/account.service.ts b/jhipster/src/main/webapp/app/shared/auth/account.service.ts new file mode 100644 index 000000000000..6d21943d497f --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/auth/account.service.ts @@ -0,0 +1,16 @@ +import { Injectable } from '@angular/core'; +import { Http, Response } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +@Injectable() +export class AccountService { + constructor(private http: Http) { } + + get(): Observable { + return this.http.get('api/account').map((res: Response) => res.json()); + } + + save(account: any): Observable { + return this.http.post('api/account', account); + } +} diff --git a/jhipster/src/main/webapp/app/shared/auth/auth-jwt.service.ts b/jhipster/src/main/webapp/app/shared/auth/auth-jwt.service.ts new file mode 100644 index 000000000000..9be418d1758d --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/auth/auth-jwt.service.ts @@ -0,0 +1,61 @@ +import { Injectable } from '@angular/core'; +import { Http, Response, Headers, URLSearchParams } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; +import { LocalStorageService, SessionStorageService } from 'ng2-webstorage'; + +@Injectable() +export class AuthServerProvider { + constructor( + private http: Http, + private $localStorage: LocalStorageService, + private $sessionStorage: SessionStorageService + ) {} + + getToken () { + return this.$localStorage.retrieve('authenticationToken') || this.$sessionStorage.retrieve('authenticationToken'); + } + + login (credentials): Observable { + + let data = { + username: credentials.username, + password: credentials.password, + rememberMe: credentials.rememberMe + }; + return this.http.post('api/authenticate', data).map(authenticateSuccess.bind(this)); + + function authenticateSuccess (resp) { + let bearerToken = resp.headers.get('Authorization'); + if (bearerToken && bearerToken.slice(0, 7) === 'Bearer ') { + let jwt = bearerToken.slice(7, bearerToken.length); + this.storeAuthenticationToken(jwt, credentials.rememberMe); + return jwt; + } + } + } + + loginWithToken(jwt, rememberMe) { + if (jwt) { + this.storeAuthenticationToken(jwt, rememberMe); + return Promise.resolve(jwt); + } else { + return Promise.reject('auth-jwt-service Promise reject'); // Put appropriate error message here + } + } + + storeAuthenticationToken(jwt, rememberMe) { + if (rememberMe) { + this.$localStorage.store('authenticationToken', jwt); + } else { + this.$sessionStorage.store('authenticationToken', jwt); + } + } + + logout (): Observable { + return new Observable(observer => { + this.$localStorage.clear('authenticationToken'); + this.$sessionStorage.clear('authenticationToken'); + observer.complete(); + }); + } +} diff --git a/jhipster/src/main/webapp/app/shared/auth/auth.service.ts b/jhipster/src/main/webapp/app/shared/auth/auth.service.ts new file mode 100644 index 000000000000..9e21fb673745 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/auth/auth.service.ts @@ -0,0 +1,65 @@ +import { Injectable } from '@angular/core'; +import { Router } from '@angular/router'; + +import { LoginModalService } from '../login/login-modal.service'; +import { Principal } from './principal.service'; +import { StateStorageService } from './state-storage.service'; + +@Injectable() +export class AuthService { + + constructor( + private principal: Principal, + private stateStorageService: StateStorageService, + private loginModalService: LoginModalService, + private router: Router + ) {} + + authorize (force) { + let authReturn = this.principal.identity(force).then(authThen.bind(this)); + + return authReturn; + + function authThen () { + let isAuthenticated = this.principal.isAuthenticated(); + let toStateInfo = this.stateStorageService.getDestinationState().destination; + + // an authenticated user can't access to login and register pages + if (isAuthenticated && (toStateInfo.name === 'register' || toStateInfo.name === 'social-auth')) { + this.router.navigate(['']); + return false; + } + + // recover and clear previousState after external login redirect (e.g. oauth2) + let fromStateInfo = this.stateStorageService.getDestinationState().from; + let previousState = this.stateStorageService.getPreviousState(); + if (isAuthenticated && !fromStateInfo.name && previousState) { + this.stateStorageService.resetPreviousState(); + this.router.navigate([previousState.name], { queryParams: previousState.params }); + return false; + } + + if (toStateInfo.data.authorities && toStateInfo.data.authorities.length > 0) { + return this.principal.hasAnyAuthority(toStateInfo.data.authorities).then(hasAnyAuthority => { + if (!hasAnyAuthority) { + if (isAuthenticated) { + // user is signed in but not authorized for desired state + this.router.navigate(['accessdenied']); + } else { + // user is not authenticated. Show the state they wanted before you + // send them to the login service, so you can return them when you're done + let toStateParamsInfo = this.stateStorageService.getDestinationState().params; + this.stateStorageService.storePreviousState(toStateInfo.name, toStateParamsInfo); + // now, send them to the signin state so they can log in + this.router.navigate(['accessdenied']).then(() => { + this.loginModalService.open(); + }); + } + } + return hasAnyAuthority; + }); + } + return true; + } + } +} diff --git a/jhipster/src/main/webapp/app/shared/auth/csrf.service.ts b/jhipster/src/main/webapp/app/shared/auth/csrf.service.ts new file mode 100644 index 000000000000..6f1064112a48 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/auth/csrf.service.ts @@ -0,0 +1,13 @@ +import { Injectable } from '@angular/core'; +import { CookieService } from 'angular2-cookie/core'; + +@Injectable() +export class CSRFService { + + constructor(private cookieService: CookieService) {} + + getCSRF(name?: string) { + name = `${name ? name : 'XSRF-TOKEN'}`; + return this.cookieService.get(name); + } +} diff --git a/jhipster/src/main/webapp/app/shared/auth/has-any-authority.directive.ts b/jhipster/src/main/webapp/app/shared/auth/has-any-authority.directive.ts new file mode 100644 index 000000000000..858c105fd552 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/auth/has-any-authority.directive.ts @@ -0,0 +1,42 @@ +import { Directive, ElementRef, Input, TemplateRef, ViewContainerRef } from '@angular/core'; +import { Principal } from './principal.service'; + +/** + * @whatItDoes Conditionally includes an HTML element if current user has any + * of the authorities passed as the `expression`. + * + * @howToUse + * ``` + * ... + * + * ... + * ``` + */ +@Directive({ + selector: '[jhiHasAnyAuthority]' +}) +export class HasAnyAuthorityDirective { + + private authorities: string[]; + + constructor(private principal: Principal, private templateRef: TemplateRef, private viewContainerRef: ViewContainerRef) { + } + + @Input() + set jhiHasAnyAuthority(value: string|string[]) { + this.authorities = typeof value === 'string' ? [ value ] : value; + this.updateView(); + // Get notified each time authentication state changes. + this.principal.getAuthenticationState().subscribe(identity => this.updateView()); + } + + private updateView(): void { + this.principal.hasAnyAuthority(this.authorities).then(result => { + if (result) { + this.viewContainerRef.createEmbeddedView(this.templateRef); + } else { + this.viewContainerRef.clear(); + } + }); + } +} diff --git a/jhipster/src/main/webapp/app/shared/auth/principal.service.ts b/jhipster/src/main/webapp/app/shared/auth/principal.service.ts new file mode 100644 index 000000000000..2c7f05dd5687 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/auth/principal.service.ts @@ -0,0 +1,93 @@ +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; +import { Subject } from 'rxjs/Subject'; +import { AccountService } from './account.service'; + +@Injectable() +export class Principal { + private userIdentity: any; + private authenticated = false; + private authenticationState = new Subject(); + + constructor( + private account: AccountService + ) {} + + authenticate (identity) { + this.userIdentity = identity; + this.authenticated = identity !== null; + this.authenticationState.next(this.userIdentity); + } + + hasAnyAuthority (authorities: string[]): Promise { + if (!this.authenticated || !this.userIdentity || !this.userIdentity.authorities) { + return Promise.resolve(false); + } + + for (let i = 0; i < authorities.length; i++) { + if (this.userIdentity.authorities.indexOf(authorities[i]) !== -1) { + return Promise.resolve(true); + } + } + + return Promise.resolve(false); + } + + hasAuthority (authority: string): Promise { + if (!this.authenticated) { + return Promise.resolve(false); + } + + return this.identity().then(id => { + return Promise.resolve(id.authorities && id.authorities.indexOf(authority) !== -1); + }, () => { + return Promise.resolve(false); + }); + } + + identity (force?: boolean): Promise { + if (force === true) { + this.userIdentity = undefined; + } + + // check and see if we have retrieved the userIdentity data from the server. + // if we have, reuse it by immediately resolving + if (this.userIdentity) { + return Promise.resolve(this.userIdentity); + } + + // retrieve the userIdentity data from the server, update the identity object, and then resolve. + return this.account.get().toPromise().then(account => { + if (account) { + this.userIdentity = account; + this.authenticated = true; + } else { + this.userIdentity = null; + this.authenticated = false; + } + this.authenticationState.next(this.userIdentity); + return this.userIdentity; + }).catch(err => { + this.userIdentity = null; + this.authenticated = false; + this.authenticationState.next(this.userIdentity); + return null; + }); + } + + isAuthenticated (): boolean { + return this.authenticated; + } + + isIdentityResolved (): boolean { + return this.userIdentity !== undefined; + } + + getAuthenticationState(): Observable { + return this.authenticationState.asObservable(); + } + + getImageUrl(): String { + return this.isIdentityResolved () ? this.userIdentity.imageUrl : null; + } +} diff --git a/jhipster/src/main/webapp/app/shared/auth/state-storage.service.ts b/jhipster/src/main/webapp/app/shared/auth/state-storage.service.ts new file mode 100644 index 000000000000..1fe364b06db0 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/auth/state-storage.service.ts @@ -0,0 +1,40 @@ +import { Injectable } from '@angular/core'; +import { SessionStorageService } from 'ng2-webstorage'; + +@Injectable() +export class StateStorageService { + constructor( + private $sessionStorage: SessionStorageService + ) {} + + getPreviousState() { + return this.$sessionStorage.retrieve('previousState'); + } + + resetPreviousState() { + this.$sessionStorage.clear('previousState'); + } + + storePreviousState(previousStateName, previousStateParams) { + let previousState = { 'name': previousStateName, 'params': previousStateParams }; + this.$sessionStorage.store('previousState', previousState); + } + + getDestinationState() { + return this.$sessionStorage.retrieve('destinationState'); + } + + storeDestinationState(destinationState, destinationStateParams, fromState) { + let destinationInfo = { + 'destination': { + 'name': destinationState.name, + 'data': destinationState.data, + }, + 'params': destinationStateParams, + 'from': { + 'name': fromState.name, + } + }; + this.$sessionStorage.store('destinationState', destinationInfo); + } +} diff --git a/jhipster/src/main/webapp/app/shared/auth/user-route-access-service.ts b/jhipster/src/main/webapp/app/shared/auth/user-route-access-service.ts new file mode 100644 index 000000000000..95eb236672c5 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/auth/user-route-access-service.ts @@ -0,0 +1,15 @@ +import { Injectable } from '@angular/core'; +import { CanActivate, Router, ActivatedRouteSnapshot } from '@angular/router'; + +import { AuthService } from '../'; + +@Injectable() +export class UserRouteAccessService implements CanActivate { + + constructor(private router: Router, private auth: AuthService) { + } + + canActivate(route: ActivatedRouteSnapshot): boolean | Promise { + return this.auth.authorize(false).then( canActivate => canActivate); + } +} diff --git a/jhipster/src/main/webapp/app/shared/constants/pagination.constants.ts b/jhipster/src/main/webapp/app/shared/constants/pagination.constants.ts new file mode 100644 index 000000000000..a148d4579ba3 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/constants/pagination.constants.ts @@ -0,0 +1 @@ +export const ITEMS_PER_PAGE = 20; diff --git a/jhipster/src/main/webapp/app/shared/index.ts b/jhipster/src/main/webapp/app/shared/index.ts new file mode 100644 index 000000000000..d2687cf88457 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/index.ts @@ -0,0 +1,23 @@ +export * from './alert/alert.component'; +export * from './alert/alert-error.component'; +export * from './auth/csrf.service'; +export * from './auth/state-storage.service'; +export * from './auth/account.service'; +export * from './auth/auth-jwt.service'; +export * from './auth/auth.service'; +export * from './auth/principal.service'; +export * from './auth/has-any-authority.directive'; +export * from './language/language.constants'; +export * from './language/language.helper'; +export * from './language/language.pipe'; +export * from './login/login.component'; +export * from './login/login.service'; +export * from './login/login-modal.service'; +export * from './constants/pagination.constants'; +export * from './user/account.model'; +export * from './user/user.model'; +export * from './user/user.service'; +export * from './shared-libs.module'; +export * from './shared-common.module'; +export * from './shared.module'; +export * from './auth/user-route-access-service'; diff --git a/jhipster/src/main/webapp/app/shared/language/language.constants.ts b/jhipster/src/main/webapp/app/shared/language/language.constants.ts new file mode 100644 index 000000000000..2292ef4624a5 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/language/language.constants.ts @@ -0,0 +1,8 @@ +/* + Languages codes are ISO_639-1 codes, see http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes + They are written in English to avoid character encoding issues (not a perfect solution) +*/ +export const LANGUAGES: string[] = [ + 'en' + // jhipster-needle-i18n-language-constant - JHipster will add/remove languages in this array +]; diff --git a/jhipster/src/main/webapp/app/shared/language/language.helper.ts b/jhipster/src/main/webapp/app/shared/language/language.helper.ts new file mode 100644 index 000000000000..d8d609bf0c77 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/language/language.helper.ts @@ -0,0 +1,53 @@ +import { Injectable } from '@angular/core'; +import { Title } from '@angular/platform-browser'; +import { Router, ActivatedRouteSnapshot } from '@angular/router'; +import { TranslateService, TranslationChangeEvent, LangChangeEvent } from 'ng2-translate/ng2-translate'; + +import { LANGUAGES } from './language.constants'; + +@Injectable() +export class JhiLanguageHelper { + + constructor (private translateService: TranslateService, private titleService: Title, private router: Router) { + this.init(); + } + + getAll(): Promise { + return Promise.resolve(LANGUAGES); + } + + /** + * Update the window title using params in the following + * order: + * 1. titleKey parameter + * 2. $state.$current.data.pageTitle (current state page title) + * 3. 'global.title' + */ + updateTitle(titleKey?: string) { + if (!titleKey) { + titleKey = this.getPageTitle(this.router.routerState.snapshot.root); + } + + this.translateService.get(titleKey).subscribe(title => { + this.titleService.setTitle(title); + }); + } + + private init () { + this.translateService.onTranslationChange.subscribe((event: TranslationChangeEvent) => { + this.updateTitle(); + }); + + this.translateService.onLangChange.subscribe((event: LangChangeEvent) => { + this.updateTitle(); + }); + } + + private getPageTitle(routeSnapshot: ActivatedRouteSnapshot) { + let title: string = (routeSnapshot.data && routeSnapshot.data['pageTitle']) ? routeSnapshot.data['pageTitle'] : 'baeldungApp'; + if (routeSnapshot.firstChild) { + title = this.getPageTitle(routeSnapshot.firstChild) || title; + } + return title; + } +} diff --git a/jhipster/src/main/webapp/app/shared/language/language.pipe.ts b/jhipster/src/main/webapp/app/shared/language/language.pipe.ts new file mode 100644 index 000000000000..d271c8e2c220 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/language/language.pipe.ts @@ -0,0 +1,42 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({name: 'findLanguageFromKey'}) +export class FindLanguageFromKeyPipe implements PipeTransform { + private languages: any = { + 'ca': 'Català', + 'cs': 'Český', + 'da': 'Dansk', + 'de': 'Deutsch', + 'el': 'Ελληνικά', + 'en': 'English', + 'es': 'Español', + 'et': 'Eesti', + 'fr': 'Français', + 'gl': 'Galego', + 'hu': 'Magyar', + 'hi': 'हिंदी', + 'hy': 'Հայերեն', + 'it': 'Italiano', + 'ja': '日本語', + 'ko': '한국어', + 'mr': 'मराठी', + 'nl': 'Nederlands', + 'pl': 'Polski', + 'pt-br': 'Português (Brasil)', + 'pt-pt': 'Português', + 'ro': 'Română', + 'ru': 'Русский', + 'sk': 'Slovenský', + 'sr': 'Srpski', + 'sv': 'Svenska', + 'ta': 'தமிழ்', + 'th': 'ไทย', + 'tr': 'Türkçe', + 'vi': 'Tiếng Việt', + 'zh-cn': '中文(简体)', + 'zh-tw': '繁體中文' + }; + transform(lang: string): string { + return this.languages[lang]; + } +} diff --git a/jhipster/src/main/webapp/app/shared/login/login-modal.service.ts b/jhipster/src/main/webapp/app/shared/login/login-modal.service.ts new file mode 100644 index 000000000000..9e435978eaa6 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/login/login-modal.service.ts @@ -0,0 +1,26 @@ +import { Injectable } from '@angular/core'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; + +import { JhiLoginModalComponent } from './login.component'; + +@Injectable() +export class LoginModalService { + private isOpen = false; + constructor ( + private modalService: NgbModal, + ) {} + + open (): NgbModalRef { + if (this.isOpen) { + return; + } + this.isOpen = true; + let modalRef = this.modalService.open(JhiLoginModalComponent); + modalRef.result.then(result => { + this.isOpen = false; + }, (reason) => { + this.isOpen = false; + }); + return modalRef; + } +} diff --git a/jhipster/src/main/webapp/app/shared/login/login.component.html b/jhipster/src/main/webapp/app/shared/login/login.component.html new file mode 100644 index 000000000000..a6b6b1924914 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/login/login.component.html @@ -0,0 +1,46 @@ + + diff --git a/jhipster/src/main/webapp/app/shared/login/login.component.ts b/jhipster/src/main/webapp/app/shared/login/login.component.ts new file mode 100644 index 000000000000..90acbb03ac33 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/login/login.component.ts @@ -0,0 +1,90 @@ +import { Component, OnInit, AfterViewInit, Renderer, ElementRef } from '@angular/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { Router } from '@angular/router'; +import { JhiLanguageService, EventManager } from 'ng-jhipster'; + +import { LoginService } from '../login/login.service'; +import { StateStorageService } from '../auth/state-storage.service'; + +@Component({ + selector: 'jhi-login-modal', + templateUrl: './login.component.html' +}) +export class JhiLoginModalComponent implements OnInit, AfterViewInit { + authenticationError: boolean; + password: string; + rememberMe: boolean; + username: string; + credentials: any; + + constructor( + private eventManager: EventManager, + private languageService: JhiLanguageService, + private loginService: LoginService, + private stateStorageService: StateStorageService, + private elementRef: ElementRef, + private renderer: Renderer, + private router: Router, + public activeModal: NgbActiveModal + ) { + this.credentials = {}; + } + + ngOnInit() { + this.languageService.addLocation('login'); + } + + ngAfterViewInit() { + this.renderer.invokeElementMethod(this.elementRef.nativeElement.querySelector('#username'), 'focus', []); + } + + cancel () { + this.credentials = { + username: null, + password: null, + rememberMe: true + }; + this.authenticationError = false; + this.activeModal.dismiss('cancel'); + } + + login () { + this.loginService.login({ + username: this.username, + password: this.password, + rememberMe: this.rememberMe + }).then(() => { + this.authenticationError = false; + this.activeModal.dismiss('login success'); + if (this.router.url === '/register' || (/activate/.test(this.router.url)) || + this.router.url === '/finishReset' || this.router.url === '/requestReset') { + this.router.navigate(['']); + } + + this.eventManager.broadcast({ + name: 'authenticationSuccess', + content: 'Sending Authentication Success' + }); + + // // previousState was set in the authExpiredInterceptor before being redirected to login modal. + // // since login is succesful, go to stored previousState and clear previousState + let previousState = this.stateStorageService.getPreviousState(); + if (previousState) { + this.stateStorageService.resetPreviousState(); + this.router.navigate([previousState.name], { queryParams: previousState.params }); + } + }).catch(() => { + this.authenticationError = true; + }); + } + + register () { + this.activeModal.dismiss('to state register'); + this.router.navigate(['/register']); + } + + requestResetPassword () { + this.activeModal.dismiss('to state requestReset'); + this.router.navigate(['/reset', 'request']); + } +} diff --git a/jhipster/src/main/webapp/app/shared/login/login.service.ts b/jhipster/src/main/webapp/app/shared/login/login.service.ts new file mode 100644 index 000000000000..22352992258b --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/login/login.service.ts @@ -0,0 +1,45 @@ +import { Injectable } from '@angular/core'; +import { JhiLanguageService } from 'ng-jhipster'; + +import { Principal } from '../auth/principal.service'; +import { AuthServerProvider } from '../auth/auth-jwt.service'; + +@Injectable() +export class LoginService { + + constructor ( + private languageService: JhiLanguageService, + private principal: Principal, + private authServerProvider: AuthServerProvider + ) {} + + login (credentials, callback?) { + let cb = callback || function() {}; + + return new Promise((resolve, reject) => { + this.authServerProvider.login(credentials).subscribe(data => { + this.principal.identity(true).then(account => { + // After the login the language will be changed to + // the language selected by the user during his registration + if (account !== null) { + this.languageService.changeLanguage(account.langKey); + } + resolve(data); + }); + return cb(); + }, err => { + this.logout(); + reject(err); + return cb(err); + }); + }); + } + loginWithToken(jwt, rememberMe) { + return this.authServerProvider.loginWithToken(jwt, rememberMe); + } + + logout () { + this.authServerProvider.logout().subscribe(); + this.principal.authenticate(null); + } +} diff --git a/jhipster/src/main/webapp/app/shared/shared-common.module.ts b/jhipster/src/main/webapp/app/shared/shared-common.module.ts new file mode 100644 index 000000000000..6f713e216bb1 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/shared-common.module.ts @@ -0,0 +1,47 @@ +import { NgModule, Sanitizer } from '@angular/core'; +import { Title } from '@angular/platform-browser'; + +import { TranslateService } from 'ng2-translate'; +import { AlertService } from 'ng-jhipster'; + +import { + BaeldungSharedLibsModule, + JhiLanguageHelper, + FindLanguageFromKeyPipe, + JhiAlertComponent, + JhiAlertErrorComponent +} from './'; + + +export function alertServiceProvider(sanitizer: Sanitizer, translateService: TranslateService) { + // set below to true to make alerts look like toast + let isToast = false; + return new AlertService(sanitizer, isToast, translateService); +} + +@NgModule({ + imports: [ + BaeldungSharedLibsModule + ], + declarations: [ + FindLanguageFromKeyPipe, + JhiAlertComponent, + JhiAlertErrorComponent + ], + providers: [ + JhiLanguageHelper, + { + provide: AlertService, + useFactory: alertServiceProvider, + deps: [Sanitizer, TranslateService] + }, + Title + ], + exports: [ + BaeldungSharedLibsModule, + FindLanguageFromKeyPipe, + JhiAlertComponent, + JhiAlertErrorComponent + ] +}) +export class BaeldungSharedCommonModule {} diff --git a/jhipster/src/main/webapp/app/shared/shared-libs.module.ts b/jhipster/src/main/webapp/app/shared/shared-libs.module.ts new file mode 100644 index 000000000000..0bf10eeaa800 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/shared-libs.module.ts @@ -0,0 +1,27 @@ +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { HttpModule } from '@angular/http'; +import { CommonModule } from '@angular/common'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { NgJhipsterModule } from 'ng-jhipster'; +import { InfiniteScrollModule } from 'angular2-infinite-scroll'; + +@NgModule({ + imports: [ + NgbModule.forRoot(), + NgJhipsterModule.forRoot({ + i18nEnabled: true, + defaultI18nLang: 'en' + }), + InfiniteScrollModule + ], + exports: [ + FormsModule, + HttpModule, + CommonModule, + NgbModule, + NgJhipsterModule, + InfiniteScrollModule + ] +}) +export class BaeldungSharedLibsModule {} diff --git a/jhipster/src/main/webapp/app/shared/shared.module.ts b/jhipster/src/main/webapp/app/shared/shared.module.ts new file mode 100644 index 000000000000..f7af13852be8 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/shared.module.ts @@ -0,0 +1,53 @@ +import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { DatePipe } from '@angular/common'; + +import { CookieService } from 'angular2-cookie/services/cookies.service'; +import { + BaeldungSharedLibsModule, + BaeldungSharedCommonModule, + CSRFService, + AuthService, + AuthServerProvider, + AccountService, + UserService, + StateStorageService, + LoginService, + LoginModalService, + Principal, + HasAnyAuthorityDirective, + JhiLoginModalComponent +} from './'; + +@NgModule({ + imports: [ + BaeldungSharedLibsModule, + BaeldungSharedCommonModule + ], + declarations: [ + JhiLoginModalComponent, + HasAnyAuthorityDirective + ], + providers: [ + CookieService, + LoginService, + LoginModalService, + AccountService, + StateStorageService, + Principal, + CSRFService, + AuthServerProvider, + AuthService, + UserService, + DatePipe + ], + entryComponents: [JhiLoginModalComponent], + exports: [ + BaeldungSharedCommonModule, + JhiLoginModalComponent, + HasAnyAuthorityDirective, + DatePipe + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] + +}) +export class BaeldungSharedModule {} diff --git a/jhipster/src/main/webapp/app/shared/user/account.model.ts b/jhipster/src/main/webapp/app/shared/user/account.model.ts new file mode 100644 index 000000000000..c8b975076039 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/user/account.model.ts @@ -0,0 +1,12 @@ +export class Account { + constructor( + public activated: boolean, + public authorities: string[], + public email: string, + public firstName: string, + public langKey: string, + public lastName: string, + public login: string, + public imageUrl: string + ) { } +} diff --git a/jhipster/src/main/webapp/app/shared/user/user.model.ts b/jhipster/src/main/webapp/app/shared/user/user.model.ts new file mode 100644 index 000000000000..374c3ae0caae --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/user/user.model.ts @@ -0,0 +1,44 @@ +export class User { + public id?: any; + public login?: string; + public firstName?: string; + public lastName?: string; + public email?: string; + public activated?: Boolean; + public langKey?: string; + public authorities?: any[]; + public createdBy?: string; + public createdDate?: Date; + public lastModifiedBy?: string; + public lastModifiedDate?: Date; + public password?: string; + constructor( + id?: any, + login?: string, + firstName?: string, + lastName?: string, + email?: string, + activated?: Boolean, + langKey?: string, + authorities?: any[], + createdBy?: string, + createdDate?: Date, + lastModifiedBy?: string, + lastModifiedDate?: Date, + password?: string + ) { + this.id = id ? id : null; + this.login = login ? login : null; + this.firstName = firstName ? firstName : null; + this.lastName = lastName ? lastName : null; + this.email = email ? email : null; + this.activated = activated ? activated : false; + this.langKey = langKey ? langKey : null; + this.authorities = authorities ? authorities : null; + this.createdBy = createdBy ? createdBy : null; + this.createdDate = createdDate ? createdDate : null; + this.lastModifiedBy = lastModifiedBy ? lastModifiedBy : null; + this.lastModifiedDate = lastModifiedDate ? lastModifiedDate : null; + this.password = password ? password : null; + } +} diff --git a/jhipster/src/main/webapp/app/shared/user/user.service.ts b/jhipster/src/main/webapp/app/shared/user/user.service.ts new file mode 100644 index 000000000000..b0e11212158a --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/user/user.service.ts @@ -0,0 +1,45 @@ +import { Injectable } from '@angular/core'; +import { Http, Response, URLSearchParams } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +import { User } from './user.model'; + +@Injectable() +export class UserService { + private resourceUrl = 'api/users'; + + constructor(private http: Http) { } + + create(user: User): Observable { + return this.http.post(this.resourceUrl, user); + } + + update(user: User): Observable { + return this.http.put(this.resourceUrl, user); + } + + find(login: string): Observable { + return this.http.get(`${this.resourceUrl}/${login}`).map((res: Response) => res.json()); + } + + query(req?: any): Observable { + let params: URLSearchParams = new URLSearchParams(); + if (req) { + params.set('page', req.page); + params.set('size', req.size); + if (req.sort) { + params.paramsMap.set('sort', req.sort); + } + } + + let options = { + search: params + }; + + return this.http.get(this.resourceUrl, options); + } + + delete(login: string): Observable { + return this.http.delete(`${this.resourceUrl}/${login}`); + } +} diff --git a/jhipster/src/main/webapp/app/vendor.ts b/jhipster/src/main/webapp/app/vendor.ts new file mode 100644 index 000000000000..f9ccfd5b0000 --- /dev/null +++ b/jhipster/src/main/webapp/app/vendor.ts @@ -0,0 +1,3 @@ +/* after changing this file run 'npm install' or 'npm run webpack:build' */ +/* tslint:disable */ +import '../content/scss/vendor.scss'; diff --git a/jhipster/src/main/webapp/content/images/hipster.png b/jhipster/src/main/webapp/content/images/hipster.png new file mode 100644 index 000000000000..141778d48252 Binary files /dev/null and b/jhipster/src/main/webapp/content/images/hipster.png differ diff --git a/jhipster/src/main/webapp/content/images/hipster2x.png b/jhipster/src/main/webapp/content/images/hipster2x.png new file mode 100644 index 000000000000..4751c91680c4 Binary files /dev/null and b/jhipster/src/main/webapp/content/images/hipster2x.png differ diff --git a/jhipster/src/main/webapp/content/images/logo-jhipster.png b/jhipster/src/main/webapp/content/images/logo-jhipster.png new file mode 100644 index 000000000000..d8eb48da05c1 Binary files /dev/null and b/jhipster/src/main/webapp/content/images/logo-jhipster.png differ diff --git a/jhipster/src/main/webapp/content/scss/global.scss b/jhipster/src/main/webapp/content/scss/global.scss new file mode 100644 index 000000000000..c3699402e609 --- /dev/null +++ b/jhipster/src/main/webapp/content/scss/global.scss @@ -0,0 +1,211 @@ + +/* ============================================================== +Bootstrap tweaks +===============================================================*/ + +body, h1, h2, h3, h4 { + font-weight: 300; +} + + +/* ========================================================================== +Browser Upgrade Prompt +========================================================================== */ +.browserupgrade { + margin: 0.2em 0; + background: #ccc; + color: #000; + padding: 0.2em 0; +} + +/* ========================================================================== +Generic styles +========================================================================== */ + +/* Error highlight on input fields */ +.ng-valid[required], .ng-valid.required { + border-left: 5px solid green; +} + +.ng-invalid:not(form) { + border-left: 5px solid red; +} + +/* other generic styles */ + +.jh-card { + padding: 1.5%; + margin-top: 20px; + border: none; +} + +.error { + color: white; + background-color: red; +} + +.pad { + padding: 10px; +} + +.w-40 { + width: 40% !important; +} + +.w-60 { + width: 60% !important; +} + +.break { + white-space: normal; + word-break:break-all; +} + +.readonly { + background-color: #eee; + opacity: 1; +} + +/*FIXME this is to support grids in table class; */ +table td[class*=col-], table th[class*=col-] { + position: static; + display: table-cell; + float: none; +} + +.footer { + border-top: 1px solid rgba(0,0,0,.125); +} + +/* ========================================================================== +make sure browsers use the pointer cursor for anchors, even with no href +========================================================================== */ +a:hover { + cursor: pointer; +} + +.hand { + cursor: pointer; +} + +/* ========================================================================== +Custom alerts for notification +========================================================================== */ +.alerts { + .alert { + text-overflow: ellipsis; + pre{ + background: none; + border: none; + font: inherit; + color: inherit; + padding: 0; + margin: 0; + } + .popover pre { + font-size: 10px; + } + } + .toast { + position: fixed; + width: 100%; + &.left { + left: 5px; + } + &.right { + right: 5px; + } + &.top { + top: 55px; + } + &.bottom { + bottom: 55px; + } + } +} + +@media screen and (min-width: 480px) { + .alerts .toast { + width: 50%; + } +} + +/* ========================================================================== +entity tables helpers +========================================================================== */ + +/* Remove Bootstrap padding from the element + http://stackoverflow.com/questions/19562903/remove-padding-from-columns-in-bootstrap-3 */ +@mixin no-padding($side) { + @if $side == 'all' { + .no-padding { + padding: 0 !important; + } + } @else { + .no-padding-#{$side} { + padding-#{$side}: 0 !important; + } + } +} +@include no-padding("left"); +@include no-padding("right"); +@include no-padding("top"); +@include no-padding("bottom"); +@include no-padding("all"); + +/* bootstrap 3 input-group 100% width + http://stackoverflow.com/questions/23436430/bootstrap-3-input-group-100-width */ +.width-min { width: 1% !important; } + +/* Makes toolbar not wrap on smaller screens + http://www.sketchingwithcss.com/samplechapter/cheatsheet.html#right */ +.flex-btn-group-container { + display: -webkit-flex; + display: flex; + -webkit-flex-direction: row; + flex-direction: row; + -webkit-justify-content: flex-end; + justify-content: flex-end; +} + +/* ========================================================================== +entity detail page css +========================================================================== */ +.row.jh-entity-details > { + dd { + margin-bottom: 15px; + } +} + +@media screen and (min-width: 768px) { + .row.jh-entity-details > { + dt { + margin-bottom: 15px; + } + dd { + border-bottom: 1px solid #eee; + padding-left: 180px; + margin-left: 0; + } + } +} + +/* ========================================================================== +ui bootstrap tweaks +========================================================================== */ +.nav, .pagination, .carousel, .panel-title a { + cursor: pointer; +} + +.datetime-picker-dropdown > li.date-picker-menu div > table .btn-default, +.uib-datepicker-popup > li > div.uib-datepicker > table .btn-default { + border: 0; +} + +.datetime-picker-dropdown > li.date-picker-menu div > table:focus, +.uib-datepicker-popup > li > div.uib-datepicker > table:focus { + outline: none; +} + + +/* jhipster-needle-scss-add-main JHipster will add new css style */ diff --git a/jhipster/src/main/webapp/content/scss/vendor.scss b/jhipster/src/main/webapp/content/scss/vendor.scss new file mode 100644 index 000000000000..55990bbef599 --- /dev/null +++ b/jhipster/src/main/webapp/content/scss/vendor.scss @@ -0,0 +1,10 @@ +/* after changing this file run 'npm install' or 'npm run webpack:build' */ +$fa-font-path: '~font-awesome/fonts'; + +/*************************** +put Sass variables here: +eg $input-color: red; +****************************/ + +@import 'node_modules/bootstrap/scss/bootstrap'; +@import 'node_modules/font-awesome/scss/font-awesome'; diff --git a/jhipster/src/main/webapp/favicon.ico b/jhipster/src/main/webapp/favicon.ico new file mode 100644 index 000000000000..44c7595f58f5 Binary files /dev/null and b/jhipster/src/main/webapp/favicon.ico differ diff --git a/jhipster/src/main/webapp/i18n/en/activate.json b/jhipster/src/main/webapp/i18n/en/activate.json new file mode 100644 index 000000000000..e766161398f0 --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/activate.json @@ -0,0 +1,9 @@ +{ + "activate": { + "title": "Activation", + "messages": { + "success": "Your user has been activated. Please ", + "error": "Your user could not be activated. Please use the registration form to sign up." + } + } +} diff --git a/jhipster/src/main/webapp/i18n/en/audits.json b/jhipster/src/main/webapp/i18n/en/audits.json new file mode 100644 index 000000000000..ed5e16d4c544 --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/audits.json @@ -0,0 +1,27 @@ +{ + "audits": { + "title": "Audits", + "filter": { + "title": "Filter per date", + "from": "from", + "to": "to", + "button": { + "weeks": "Weeks", + "today": "today", + "clear": "clear", + "close": "close" + } + }, + "table": { + "header": { + "principal": "User", + "date": "Date", + "status": "State", + "data": "Extra data" + }, + "data": { + "remoteAddress": "Remote Address:" + } + } + } +} diff --git a/jhipster/src/main/webapp/i18n/en/comment.json b/jhipster/src/main/webapp/i18n/en/comment.json new file mode 100644 index 000000000000..eed148855a55 --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/comment.json @@ -0,0 +1,23 @@ +{ + "baeldungApp": { + "comment" : { + "home": { + "title": "Comments", + "createLabel": "Create a new Comment", + "createOrEditLabel": "Create or edit a Comment" + }, + "created": "A new Comment is created with identifier {{ param }}", + "updated": "A Comment is updated with identifier {{ param }}", + "deleted": "A Comment is deleted with identifier {{ param }}", + "delete": { + "question": "Are you sure you want to delete Comment {{ id }}?" + }, + "detail": { + "title": "Comment" + }, + "text": "Text", + "creationDate": "Creation Date", + "post": "Post" + } + } +} diff --git a/jhipster/src/main/webapp/i18n/en/configuration.json b/jhipster/src/main/webapp/i18n/en/configuration.json new file mode 100644 index 000000000000..81e208de5c0b --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/configuration.json @@ -0,0 +1,10 @@ +{ + "configuration": { + "title": "Configuration", + "filter": "Filter (by prefix)", + "table": { + "prefix": "Prefix", + "properties": "Properties" + } + } +} diff --git a/jhipster/src/main/webapp/i18n/en/error.json b/jhipster/src/main/webapp/i18n/en/error.json new file mode 100644 index 000000000000..65246c3b0308 --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/error.json @@ -0,0 +1,6 @@ +{ + "error": { + "title": "Error page!", + "403": "You are not authorized to access the page." + } +} diff --git a/jhipster/src/main/webapp/i18n/en/gateway.json b/jhipster/src/main/webapp/i18n/en/gateway.json new file mode 100644 index 000000000000..8c8bbd7e981c --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/gateway.json @@ -0,0 +1,15 @@ +{ + "gateway": { + "title": "Gateway", + "routes": { + "title": "Current routes", + "url": "URL", + "service": "service", + "servers": "Available servers", + "error": "Warning: no server available!" + }, + "refresh": { + "button": "Refresh" + } + } +} diff --git a/jhipster/src/main/webapp/i18n/en/global.json b/jhipster/src/main/webapp/i18n/en/global.json new file mode 100644 index 000000000000..0c49362336cf --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/global.json @@ -0,0 +1,133 @@ +{ + "global": { + "title": "Baeldung", + "browsehappy": "You are using an outdated browser. Please upgrade your browser to improve your experience.", + "menu": { + "home": "Home", + "jhipster-needle-menu-add-element": "JHipster will add additional menu entries here (do not translate!)", + "entities": { + "main": "Entities", + "post": "Post", + "comment": "Comment", + "jhipster-needle-menu-add-entry": "JHipster will add additional entities here (do not translate!)" + }, + "account": { + "main": "Account", + "settings": "Settings", + "password": "Password", + "sessions": "Sessions", + "login": "Sign in", + "logout": "Sign out", + "register": "Register" + }, + "admin": { + "main": "Administration", + "userManagement": "User management", + "tracker": "User tracker", + "metrics": "Metrics", + "health": "Health", + "configuration": "Configuration", + "logs": "Logs", + "audits": "Audits", + "apidocs": "API", + "database": "Database", + "jhipster-needle-menu-add-admin-element": "JHipster will add additional menu entries here (do not translate!)" + }, + "language": "Language" + }, + "form": { + "username": "Username", + "username.placeholder": "Your username", + "newpassword": "New password", + "newpassword.placeholder": "New password", + "confirmpassword": "New password confirmation", + "confirmpassword.placeholder": "Confirm the new password", + "email": "E-mail", + "email.placeholder": "Your e-mail" + }, + "messages": { + "info": { + "authenticated": { + "prefix": "If you want to ", + "link": "sign in", + "suffix": ", you can try the default accounts:
- Administrator (login=\"admin\" and password=\"admin\")
- User (login=\"user\" and password=\"user\")." + }, + "register": { + "noaccount": "You don't have an account yet?", + "link": "Register a new account" + } + }, + "error": { + "dontmatch": "The password and its confirmation do not match!" + }, + "validate": { + "newpassword": { + "required": "Your password is required.", + "minlength": "Your password is required to be at least 4 characters.", + "maxlength": "Your password cannot be longer than 50 characters.", + "strength": "Password strength:" + }, + "confirmpassword": { + "required": "Your confirmation password is required.", + "minlength": "Your confirmation password is required to be at least 4 characters.", + "maxlength": "Your confirmation password cannot be longer than 50 characters." + }, + "email": { + "required": "Your e-mail is required.", + "invalid": "Your e-mail is invalid.", + "minlength": "Your e-mail is required to be at least 5 characters.", + "maxlength": "Your e-mail cannot be longer than 50 characters." + } + } + }, + "field": { + "id": "ID" + }, + "ribbon": { + "dev":"Development" + } + }, + "entity": { + "action": { + "addblob": "Add blob", + "addimage": "Add image", + "back": "Back", + "cancel": "Cancel", + "delete": "Delete", + "edit": "Edit", + "open": "Open", + "save": "Save", + "view": "View" + }, + "detail": { + "field": "Field", + "value": "Value" + }, + "delete": { + "title": "Confirm delete operation" + }, + "validation": { + "required": "This field is required.", + "minlength": "This field is required to be at least {{ min }} characters.", + "maxlength": "This field cannot be longer than {{ max }} characters.", + "min": "This field should be at least {{ min }}.", + "max": "This field cannot be more than {{ max }}.", + "minbytes": "This field should be at least {{ min }} bytes.", + "maxbytes": "This field cannot be more than {{ max }} bytes.", + "pattern": "This field should follow pattern {{ pattern }}.", + "number": "This field should be a number.", + "datetimelocal": "This field should be a date and time." + } + }, + "error": { + "internalServerError": "Internal server error", + "server.not.reachable": "Server not reachable", + "url.not.found": "Not found", + "NotNull": "Field {{ fieldName }} cannot be empty!", + "Size": "Field {{ fieldName }} does not meet min/max size requirements!", + "userexists": "Login name already used!", + "emailexists": "E-mail is already in use!", + "idexists": "A new {{ entityName }} cannot already have an ID" + }, + "footer": "This is your footer" +} diff --git a/jhipster/src/main/webapp/i18n/en/health.json b/jhipster/src/main/webapp/i18n/en/health.json new file mode 100644 index 000000000000..868ea3fda4f0 --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/health.json @@ -0,0 +1,27 @@ +{ + "health": { + "title": "Health Checks", + "refresh.button": "Refresh", + "stacktrace": "Stacktrace", + "details": { + "details": "Details", + "properties": "Properties", + "name": "Name", + "value": "Value", + "error": "Error" + }, + "indicator": { + "diskSpace": "Disk space", + "mail": "Email", + "db": "Database" + }, + "table": { + "service": "Service name", + "status": "Status" + }, + "status": { + "UP": "UP", + "DOWN": "DOWN" + } + } +} diff --git a/jhipster/src/main/webapp/i18n/en/home.json b/jhipster/src/main/webapp/i18n/en/home.json new file mode 100644 index 000000000000..402f18700ad9 --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/home.json @@ -0,0 +1,19 @@ +{ + "home": { + "title": "Welcome, Java Hipster!", + "subtitle": "This is your homepage", + "logged": { + "message": "You are logged in as user \"{{username}}\"." + }, + "question": "If you have any question on JHipster:", + "link": { + "homepage": "JHipster homepage", + "stackoverflow": "JHipster on Stack Overflow", + "bugtracker": "JHipster bug tracker", + "chat": "JHipster public chat room", + "follow": "follow @java_hipster on Twitter" + }, + "like": "If you like JHipster, don't forget to give us a star on", + "github": "GitHub" + } +} diff --git a/jhipster/src/main/webapp/i18n/en/login.json b/jhipster/src/main/webapp/i18n/en/login.json new file mode 100644 index 000000000000..3a000852e62c --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/login.json @@ -0,0 +1,19 @@ +{ + "login": { + "title": "Sign in", + "form": { + "password": "Password", + "password.placeholder": "Your password", + "rememberme": "Remember me", + "button": "Sign in" + }, + "messages": { + "error": { + "authentication": "Failed to sign in! Please check your credentials and try again." + } + }, + "password" : { + "forgot": "Did you forget your password?" + } + } +} diff --git a/jhipster/src/main/webapp/i18n/en/logs.json b/jhipster/src/main/webapp/i18n/en/logs.json new file mode 100644 index 000000000000..a614b128da59 --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/logs.json @@ -0,0 +1,11 @@ +{ + "logs": { + "title": "Logs", + "nbloggers": "There are {{ total }} loggers.", + "filter": "Filter", + "table": { + "name": "Name", + "level": "Level" + } + } +} diff --git a/jhipster/src/main/webapp/i18n/en/metrics.json b/jhipster/src/main/webapp/i18n/en/metrics.json new file mode 100644 index 000000000000..9d804947c571 --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/metrics.json @@ -0,0 +1,101 @@ +{ + "metrics": { + "title": "Application Metrics", + "refresh.button": "Refresh", + "updating": "Updating...", + "jvm": { + "title": "JVM Metrics", + "memory": { + "title": "Memory", + "total": "Total Memory", + "heap": "Heap Memory", + "nonheap": "Non-Heap Memory" + }, + "threads": { + "title": "Threads", + "all": "All", + "runnable": "Runnable", + "timedwaiting": "Timed waiting", + "waiting": "Waiting", + "blocked": "Blocked", + "dump": { + "title": "Threads dump", + "id": "Id: ", + "blockedtime": "Blocked Time", + "blockedcount": "Blocked Count", + "waitedtime": "Waited Time", + "waitedcount": "Waited Count", + "lockname": "Lock name", + "stacktrace": "Stacktrace", + "show": "Show Stacktrace", + "hide": "Hide Stacktrace" + } + }, + "gc": { + "title": "Garbage collections", + "marksweepcount": "Mark Sweep count", + "marksweeptime": "Mark Sweep time", + "scavengecount": "Scavenge count", + "scavengetime": "Scavenge time" + }, + "http": { + "title": "HTTP requests (events per second)", + "active": "Active requests:", + "total": "Total requests:", + "table": { + "code": "Code", + "count": "Count", + "mean": "Mean", + "average": "Average" + }, + "code": { + "ok": "Ok", + "notfound": "Not found", + "servererror": "Server Error" + } + } + }, + "servicesstats": { + "title": "Services statistics (time in millisecond)", + "table": { + "name": "Service name", + "count": "Count", + "mean": "Mean", + "min": "Min", + "max": "Max", + "p50": "p50", + "p75": "p75", + "p95": "p95", + "p99": "p99" + } + }, + "cache": { + "title": "Cache statistics", + "cachename": "Cache name", + "hits": "Cache Hits", + "misses": "Cache Misses", + "gets": "Cache Gets", + "puts": "Cache Puts", + "removals": "Cache Removals", + "evictions": "Cache Evictions", + "hitPercent": "Cache Hit %", + "missPercent": "Cache Miss %", + "averageGetTime": "Average get time (µs)", + "averagePutTime": "Average put time (µs)", + "averageRemoveTime": "Average remove time (µs)" + }, + "datasource": { + "usage": "Usage", + "title": "DataSource statistics (time in millisecond)", + "name": "Pool usage", + "count": "Count", + "mean": "Mean", + "min": "Min", + "max": "Max", + "p50": "p50", + "p75": "p75", + "p95": "p95", + "p99": "p99" + } + } +} diff --git a/jhipster/src/main/webapp/i18n/en/password.json b/jhipster/src/main/webapp/i18n/en/password.json new file mode 100644 index 000000000000..46227a77024c --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/password.json @@ -0,0 +1,12 @@ +{ + "password": { + "title": "Password for [{{username}}]", + "form": { + "button": "Save" + }, + "messages": { + "error": "An error has occurred! The password could not be changed.", + "success": "Password changed!" + } + } +} diff --git a/jhipster/src/main/webapp/i18n/en/post.json b/jhipster/src/main/webapp/i18n/en/post.json new file mode 100644 index 000000000000..14c64f3f903b --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/post.json @@ -0,0 +1,24 @@ +{ + "baeldungApp": { + "post" : { + "home": { + "title": "Posts", + "createLabel": "Create a new Post", + "createOrEditLabel": "Create or edit a Post" + }, + "created": "A new Post is created with identifier {{ param }}", + "updated": "A Post is updated with identifier {{ param }}", + "deleted": "A Post is deleted with identifier {{ param }}", + "delete": { + "question": "Are you sure you want to delete Post {{ id }}?" + }, + "detail": { + "title": "Post" + }, + "title": "Title", + "content": "Content", + "creationDate": "Creation Date", + "creator": "Creator" + } + } +} diff --git a/jhipster/src/main/webapp/i18n/en/register.json b/jhipster/src/main/webapp/i18n/en/register.json new file mode 100644 index 000000000000..df8f6e31b8ce --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/register.json @@ -0,0 +1,24 @@ +{ + "register": { + "title": "Registration", + "form": { + "button": "Register" + }, + "messages": { + "validate": { + "login": { + "required": "Your username is required.", + "minlength": "Your username is required to be at least 1 character.", + "maxlength": "Your username cannot be longer than 50 characters.", + "pattern": "Your username can only contain lower-case letters and digits." + } + }, + "success": "Registration saved! Please check your email for confirmation.", + "error": { + "fail": "Registration failed! Please try again later.", + "userexists": "Login name already registered! Please choose another one.", + "emailexists": "E-mail is already in use! Please choose another one." + } + } + } +} diff --git a/jhipster/src/main/webapp/i18n/en/reset.json b/jhipster/src/main/webapp/i18n/en/reset.json new file mode 100644 index 000000000000..fc61e0070f26 --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/reset.json @@ -0,0 +1,27 @@ +{ + "reset": { + "request": { + "title": "Reset your password", + "form": { + "button": "Reset password" + }, + "messages": { + "info": "Enter the e-mail address you used to register", + "success": "Check your e-mails for details on how to reset your password.", + "notfound": "E-Mail address isn't registered! Please check and try again" + } + }, + "finish" : { + "title": "Reset password", + "form": { + "button": "Validate new password" + }, + "messages": { + "info": "Choose a new password", + "success": "Your password has been reset. Please ", + "keymissing": "The reset key is missing.", + "error": "Your password couldn't be reset. Remember a password request is only valid for 24 hours." + } + } + } +} diff --git a/jhipster/src/main/webapp/i18n/en/sessions.json b/jhipster/src/main/webapp/i18n/en/sessions.json new file mode 100644 index 000000000000..d410035ee7ca --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/sessions.json @@ -0,0 +1,15 @@ +{ + "sessions": { + "title": "Active sessions for [{{username}}]", + "table": { + "ipaddress": "IP address", + "useragent": "User Agent", + "date": "Date", + "button": "Invalidate" + }, + "messages": { + "success": "Session invalidated!", + "error": "An error has occurred! The session could not be invalidated." + } + } +} diff --git a/jhipster/src/main/webapp/i18n/en/settings.json b/jhipster/src/main/webapp/i18n/en/settings.json new file mode 100644 index 000000000000..919ab51cc93c --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/settings.json @@ -0,0 +1,32 @@ +{ + "settings": { + "title": "User settings for [{{username}}]", + "form": { + "firstname": "First Name", + "firstname.placeholder": "Your first name", + "lastname": "Last Name", + "lastname.placeholder": "Your last name", + "language": "Language", + "button": "Save" + }, + "messages": { + "error": { + "fail": "An error has occurred! Settings could not be saved.", + "emailexists": "E-mail is already in use! Please choose another one." + }, + "success": "Settings saved!", + "validate": { + "firstname": { + "required": "Your first name is required.", + "minlength": "Your first name is required to be at least 1 character", + "maxlength": "Your first name cannot be longer than 50 characters" + }, + "lastname": { + "required": "Your last name is required.", + "minlength": "Your last name is required to be at least 1 character", + "maxlength": "Your last name cannot be longer than 50 characters" + } + } + } + } +} diff --git a/jhipster/src/main/webapp/i18n/en/user-management.json b/jhipster/src/main/webapp/i18n/en/user-management.json new file mode 100644 index 000000000000..30c125b6d00b --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/user-management.json @@ -0,0 +1,30 @@ +{ + "userManagement": { + "home": { + "title": "Users", + "createLabel": "Create a new user", + "createOrEditLabel": "Create or edit a user" + }, + "created": "A new user is created with identifier {{ param }}", + "updated": "An user is updated with identifier {{ param }}", + "deleted": "An user is deleted with identifier {{ param }}", + "delete": { + "question": "Are you sure you want to delete user {{ login }}?" + }, + "detail": { + "title": "User" + }, + "login": "Login", + "firstName": "First name", + "lastName": "Last name", + "email": "Email", + "activated": "Activated", + "deactivated": "Deactivated", + "profiles": "Profiles", + "langKey": "Language", + "createdBy": "Created by", + "createdDate": "Created date", + "lastModifiedBy": "Modified by", + "lastModifiedDate": "Modified date" + } +} diff --git a/jhipster/src/main/webapp/index.html b/jhipster/src/main/webapp/index.html new file mode 100644 index 000000000000..6227d628230a --- /dev/null +++ b/jhipster/src/main/webapp/index.html @@ -0,0 +1,27 @@ + + + + + + + baeldung + + + + + + + + + + diff --git a/jhipster/src/main/webapp/robots.txt b/jhipster/src/main/webapp/robots.txt new file mode 100644 index 000000000000..7de2585cf605 --- /dev/null +++ b/jhipster/src/main/webapp/robots.txt @@ -0,0 +1,11 @@ +# robotstxt.org/ + +User-agent: * +Disallow: /api/account +Disallow: /api/account/change_password +Disallow: /api/account/sessions +Disallow: /api/audits/ +Disallow: /api/logs/ +Disallow: /api/users/ +Disallow: /management/ +Disallow: /v2/api-docs/ diff --git a/jhipster/src/main/webapp/swagger-ui/images/throbber.gif b/jhipster/src/main/webapp/swagger-ui/images/throbber.gif new file mode 100644 index 000000000000..06393889242f Binary files /dev/null and b/jhipster/src/main/webapp/swagger-ui/images/throbber.gif differ diff --git a/jhipster/src/main/webapp/swagger-ui/index.html b/jhipster/src/main/webapp/swagger-ui/index.html new file mode 100644 index 000000000000..42692c0d4573 --- /dev/null +++ b/jhipster/src/main/webapp/swagger-ui/index.html @@ -0,0 +1,177 @@ + + + + + Swagger UI + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
+
+ + diff --git a/jhipster/src/test/gatling/conf/gatling.conf b/jhipster/src/test/gatling/conf/gatling.conf new file mode 100644 index 000000000000..e509853f225d --- /dev/null +++ b/jhipster/src/test/gatling/conf/gatling.conf @@ -0,0 +1,131 @@ +######################### +# Gatling Configuration # +######################### + +# This file contains all the settings configurable for Gatling with their default values + +gatling { + core { + #outputDirectoryBaseName = "" # The prefix for each simulation result folder (then suffixed by the report generation timestamp) + #runDescription = "" # The description for this simulation run, displayed in each report + #encoding = "utf-8" # Encoding to use throughout Gatling for file and string manipulation + #simulationClass = "" # The FQCN of the simulation to run (when used in conjunction with noReports, the simulation for which assertions will be validated) + #mute = false # When set to true, don't ask for simulation name nor run description (currently only used by Gatling SBT plugin) + #elFileBodiesCacheMaxCapacity = 200 # Cache size for request body EL templates, set to 0 to disable + #rawFileBodiesCacheMaxCapacity = 200 # Cache size for request body Raw templates, set to 0 to disable + #rawFileBodiesInMemoryMaxSize = 1000 # Below this limit, raw file bodies will be cached in memory + + extract { + regex { + #cacheMaxCapacity = 200 # Cache size for the compiled regexes, set to 0 to disable caching + } + xpath { + #cacheMaxCapacity = 200 # Cache size for the compiled XPath queries, set to 0 to disable caching + } + jsonPath { + #cacheMaxCapacity = 200 # Cache size for the compiled jsonPath queries, set to 0 to disable caching + #preferJackson = false # When set to true, prefer Jackson over Boon for JSON-related operations + } + css { + #cacheMaxCapacity = 200 # Cache size for the compiled CSS selectors queries, set to 0 to disable caching + } + } + directory { + #data = user-files/data # Folder where user's data (e.g. files used by Feeders) is located + #bodies = user-files/bodies # Folder where bodies are located + #simulations = user-files/simulations # Folder where the bundle's simulations are located + #reportsOnly = "" # If set, name of report folder to look for in order to generate its report + #binaries = "" # If set, name of the folder where compiles classes are located: Defaults to GATLING_HOME/target. + #results = results # Name of the folder where all reports folder are located + } + } + charting { + #noReports = false # When set to true, don't generate HTML reports + #maxPlotPerSeries = 1000 # Number of points per graph in Gatling reports + #useGroupDurationMetric = false # Switch group timings from cumulated response time to group duration. + indicators { + #lowerBound = 800 # Lower bound for the requests' response time to track in the reports and the console summary + #higherBound = 1200 # Higher bound for the requests' response time to track in the reports and the console summary + #percentile1 = 50 # Value for the 1st percentile to track in the reports, the console summary and Graphite + #percentile2 = 75 # Value for the 2nd percentile to track in the reports, the console summary and Graphite + #percentile3 = 95 # Value for the 3rd percentile to track in the reports, the console summary and Graphite + #percentile4 = 99 # Value for the 4th percentile to track in the reports, the console summary and Graphite + } + } + http { + #fetchedCssCacheMaxCapacity = 200 # Cache size for CSS parsed content, set to 0 to disable + #fetchedHtmlCacheMaxCapacity = 200 # Cache size for HTML parsed content, set to 0 to disable + #perUserCacheMaxCapacity = 200 # Per virtual user cache size, set to 0 to disable + #warmUpUrl = "http://gatling.io" # The URL to use to warm-up the HTTP stack (blank means disabled) + #enableGA = true # Very light Google Analytics, please support + ssl { + keyStore { + #type = "" # Type of SSLContext's KeyManagers store + #file = "" # Location of SSLContext's KeyManagers store + #password = "" # Password for SSLContext's KeyManagers store + #algorithm = "" # Algorithm used SSLContext's KeyManagers store + } + trustStore { + #type = "" # Type of SSLContext's TrustManagers store + #file = "" # Location of SSLContext's TrustManagers store + #password = "" # Password for SSLContext's TrustManagers store + #algorithm = "" # Algorithm used by SSLContext's TrustManagers store + } + } + ahc { + #keepAlive = true # Allow pooling HTTP connections (keep-alive header automatically added) + #connectTimeout = 10000 # Timeout when establishing a connection + #handshakeTimeout = 10000 # Timeout when performing TLS hashshake + #pooledConnectionIdleTimeout = 60000 # Timeout when a connection stays unused in the pool + #readTimeout = 60000 # Timeout when a used connection stays idle + #maxRetry = 2 # Number of times that a request should be tried again + #requestTimeout = 60000 # Timeout of the requests + #acceptAnyCertificate = true # When set to true, doesn't validate SSL certificates + #httpClientCodecMaxInitialLineLength = 4096 # Maximum length of the initial line of the response (e.g. "HTTP/1.0 200 OK") + #httpClientCodecMaxHeaderSize = 8192 # Maximum size, in bytes, of each request's headers + #httpClientCodecMaxChunkSize = 8192 # Maximum length of the content or each chunk + #webSocketMaxFrameSize = 10240000 # Maximum frame payload size + #sslEnabledProtocols = [TLSv1.2, TLSv1.1, TLSv1] # Array of enabled protocols for HTTPS, if empty use the JDK defaults + #sslEnabledCipherSuites = [] # Array of enabled cipher suites for HTTPS, if empty use the AHC defaults + #sslSessionCacheSize = 0 # SSLSession cache size, set to 0 to use JDK's default + #sslSessionTimeout = 0 # SSLSession timeout in seconds, set to 0 to use JDK's default (24h) + #useOpenSsl = false # if OpenSSL should be used instead of JSSE (requires tcnative jar) + #useNativeTransport = false # if native transport should be used instead of Java NIO (requires netty-transport-native-epoll, currently Linux only) + #tcpNoDelay = true + #soReuseAddress = false + #soLinger = -1 + #soSndBuf = -1 + #soRcvBuf = -1 + #allocator = "pooled" # switch to unpooled for unpooled ByteBufAllocator + #maxThreadLocalCharBufferSize = 200000 # Netty's default is 16k + } + dns { + #queryTimeout = 5000 # Timeout of each DNS query in millis + #maxQueriesPerResolve = 6 # Maximum allowed number of DNS queries for a given name resolution + } + } + jms { + #acknowledgedMessagesBufferSize = 5000 # size of the buffer used to tracked acknowledged messages and protect against duplicate receives + } + data { + #writers = [console, file] # The list of DataWriters to which Gatling write simulation data (currently supported : console, file, graphite, jdbc) + console { + #light = false # When set to true, displays a light version without detailed request stats + } + file { + #bufferSize = 8192 # FileDataWriter's internal data buffer size, in bytes + } + leak { + #noActivityTimeout = 30 # Period, in seconds, for which Gatling may have no activity before considering a leak may be happening + } + graphite { + #light = false # only send the all* stats + #host = "localhost" # The host where the Carbon server is located + #port = 2003 # The port to which the Carbon server listens to (2003 is default for plaintext, 2004 is default for pickle) + #protocol = "tcp" # The protocol used to send data to Carbon (currently supported : "tcp", "udp") + #rootPathPrefix = "gatling" # The common prefix of all metrics sent to Graphite + #bufferSize = 8192 # GraphiteDataWriter's internal data buffer size, in bytes + #writeInterval = 1 # GraphiteDataWriter's write interval, in seconds + } + } +} diff --git a/jhipster/src/test/gatling/conf/logback.xml b/jhipster/src/test/gatling/conf/logback.xml new file mode 100644 index 000000000000..7b037e6813a8 --- /dev/null +++ b/jhipster/src/test/gatling/conf/logback.xml @@ -0,0 +1,22 @@ + + + + + + %d{HH:mm:ss.SSS} [%-5level] %logger{15} - %msg%n%rEx + false + + + + + + + + + + + + + + + diff --git a/jhipster/src/test/gatling/simulations/CommentGatlingTest.scala b/jhipster/src/test/gatling/simulations/CommentGatlingTest.scala new file mode 100644 index 000000000000..93d066f9bb90 --- /dev/null +++ b/jhipster/src/test/gatling/simulations/CommentGatlingTest.scala @@ -0,0 +1,92 @@ +import _root_.io.gatling.core.scenario.Simulation +import ch.qos.logback.classic.{Level, LoggerContext} +import io.gatling.core.Predef._ +import io.gatling.http.Predef._ +import org.slf4j.LoggerFactory + +import scala.concurrent.duration._ + +/** + * Performance test for the Comment entity. + */ +class CommentGatlingTest extends Simulation { + + val context: LoggerContext = LoggerFactory.getILoggerFactory.asInstanceOf[LoggerContext] + // Log all HTTP requests + //context.getLogger("io.gatling.http").setLevel(Level.valueOf("TRACE")) + // Log failed HTTP requests + //context.getLogger("io.gatling.http").setLevel(Level.valueOf("DEBUG")) + + val baseURL = Option(System.getProperty("baseURL")) getOrElse """http://127.0.0.1:8080""" + + val httpConf = http + .baseURL(baseURL) + .inferHtmlResources() + .acceptHeader("*/*") + .acceptEncodingHeader("gzip, deflate") + .acceptLanguageHeader("fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3") + .connectionHeader("keep-alive") + .userAgentHeader("Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:33.0) Gecko/20100101 Firefox/33.0") + + val headers_http = Map( + "Accept" -> """application/json""" + ) + + val headers_http_authentication = Map( + "Content-Type" -> """application/json""", + "Accept" -> """application/json""" + ) + + val headers_http_authenticated = Map( + "Accept" -> """application/json""", + "Authorization" -> "${access_token}" + ) + + val scn = scenario("Test the Comment entity") + .exec(http("First unauthenticated request") + .get("/api/account") + .headers(headers_http) + .check(status.is(401))).exitHereIfFailed + .pause(10) + .exec(http("Authentication") + .post("/api/authenticate") + .headers(headers_http_authentication) + .body(StringBody("""{"username":"admin", "password":"admin"}""")).asJSON + .check(header.get("Authorization").saveAs("access_token"))).exitHereIfFailed + .pause(1) + .exec(http("Authenticated request") + .get("/api/account") + .headers(headers_http_authenticated) + .check(status.is(200))) + .pause(10) + .repeat(2) { + exec(http("Get all comments") + .get("/api/comments") + .headers(headers_http_authenticated) + .check(status.is(200))) + .pause(10 seconds, 20 seconds) + .exec(http("Create new comment") + .post("/api/comments") + .headers(headers_http_authenticated) + .body(StringBody("""{"id":null, "text":"SAMPLE_TEXT", "creationDate":"2020-01-01T00:00:00.000Z"}""")).asJSON + .check(status.is(201)) + .check(headerRegex("Location", "(.*)").saveAs("new_comment_url"))).exitHereIfFailed + .pause(10) + .repeat(5) { + exec(http("Get created comment") + .get("${new_comment_url}") + .headers(headers_http_authenticated)) + .pause(10) + } + .exec(http("Delete created comment") + .delete("${new_comment_url}") + .headers(headers_http_authenticated)) + .pause(10) + } + + val users = scenario("Users").exec(scn) + + setUp( + users.inject(rampUsers(100) over (1 minutes)) + ).protocols(httpConf) +} diff --git a/jhipster/src/test/gatling/simulations/PostGatlingTest.scala b/jhipster/src/test/gatling/simulations/PostGatlingTest.scala new file mode 100644 index 000000000000..d76198c9ae21 --- /dev/null +++ b/jhipster/src/test/gatling/simulations/PostGatlingTest.scala @@ -0,0 +1,92 @@ +import _root_.io.gatling.core.scenario.Simulation +import ch.qos.logback.classic.{Level, LoggerContext} +import io.gatling.core.Predef._ +import io.gatling.http.Predef._ +import org.slf4j.LoggerFactory + +import scala.concurrent.duration._ + +/** + * Performance test for the Post entity. + */ +class PostGatlingTest extends Simulation { + + val context: LoggerContext = LoggerFactory.getILoggerFactory.asInstanceOf[LoggerContext] + // Log all HTTP requests + //context.getLogger("io.gatling.http").setLevel(Level.valueOf("TRACE")) + // Log failed HTTP requests + //context.getLogger("io.gatling.http").setLevel(Level.valueOf("DEBUG")) + + val baseURL = Option(System.getProperty("baseURL")) getOrElse """http://127.0.0.1:8080""" + + val httpConf = http + .baseURL(baseURL) + .inferHtmlResources() + .acceptHeader("*/*") + .acceptEncodingHeader("gzip, deflate") + .acceptLanguageHeader("fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3") + .connectionHeader("keep-alive") + .userAgentHeader("Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:33.0) Gecko/20100101 Firefox/33.0") + + val headers_http = Map( + "Accept" -> """application/json""" + ) + + val headers_http_authentication = Map( + "Content-Type" -> """application/json""", + "Accept" -> """application/json""" + ) + + val headers_http_authenticated = Map( + "Accept" -> """application/json""", + "Authorization" -> "${access_token}" + ) + + val scn = scenario("Test the Post entity") + .exec(http("First unauthenticated request") + .get("/api/account") + .headers(headers_http) + .check(status.is(401))).exitHereIfFailed + .pause(10) + .exec(http("Authentication") + .post("/api/authenticate") + .headers(headers_http_authentication) + .body(StringBody("""{"username":"admin", "password":"admin"}""")).asJSON + .check(header.get("Authorization").saveAs("access_token"))).exitHereIfFailed + .pause(1) + .exec(http("Authenticated request") + .get("/api/account") + .headers(headers_http_authenticated) + .check(status.is(200))) + .pause(10) + .repeat(2) { + exec(http("Get all posts") + .get("/api/posts") + .headers(headers_http_authenticated) + .check(status.is(200))) + .pause(10 seconds, 20 seconds) + .exec(http("Create new post") + .post("/api/posts") + .headers(headers_http_authenticated) + .body(StringBody("""{"id":null, "title":"SAMPLE_TEXT", "content":"SAMPLE_TEXT", "creationDate":"2020-01-01T00:00:00.000Z"}""")).asJSON + .check(status.is(201)) + .check(headerRegex("Location", "(.*)").saveAs("new_post_url"))).exitHereIfFailed + .pause(10) + .repeat(5) { + exec(http("Get created post") + .get("${new_post_url}") + .headers(headers_http_authenticated)) + .pause(10) + } + .exec(http("Delete created post") + .delete("${new_post_url}") + .headers(headers_http_authenticated)) + .pause(10) + } + + val users = scenario("Users").exec(scn) + + setUp( + users.inject(rampUsers(100) over (1 minutes)) + ).protocols(httpConf) +} diff --git a/jhipster/src/test/java/com/baeldung/security/SecurityUtilsUnitTest.java b/jhipster/src/test/java/com/baeldung/security/SecurityUtilsUnitTest.java new file mode 100644 index 000000000000..b78a18790b5b --- /dev/null +++ b/jhipster/src/test/java/com/baeldung/security/SecurityUtilsUnitTest.java @@ -0,0 +1,50 @@ +package com.baeldung.security; + +import org.junit.Test; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; + +import java.util.ArrayList; +import java.util.Collection; + +import static org.assertj.core.api.Assertions.assertThat; + +/** +* Test class for the SecurityUtils utility class. +* +* @see SecurityUtils +*/ +public class SecurityUtilsUnitTest { + + @Test + public void testgetCurrentUserLogin() { + SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); + securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("admin", "admin")); + SecurityContextHolder.setContext(securityContext); + String login = SecurityUtils.getCurrentUserLogin(); + assertThat(login).isEqualTo("admin"); + } + + @Test + public void testIsAuthenticated() { + SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); + securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("admin", "admin")); + SecurityContextHolder.setContext(securityContext); + boolean isAuthenticated = SecurityUtils.isAuthenticated(); + assertThat(isAuthenticated).isTrue(); + } + + @Test + public void testAnonymousIsNotAuthenticated() { + SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); + Collection authorities = new ArrayList<>(); + authorities.add(new SimpleGrantedAuthority(AuthoritiesConstants.ANONYMOUS)); + securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("anonymous", "anonymous", authorities)); + SecurityContextHolder.setContext(securityContext); + boolean isAuthenticated = SecurityUtils.isAuthenticated(); + assertThat(isAuthenticated).isFalse(); + } +} diff --git a/jhipster/src/test/java/com/baeldung/security/jwt/TokenProviderTest.java b/jhipster/src/test/java/com/baeldung/security/jwt/TokenProviderTest.java new file mode 100644 index 000000000000..3fec4bfb88a1 --- /dev/null +++ b/jhipster/src/test/java/com/baeldung/security/jwt/TokenProviderTest.java @@ -0,0 +1,107 @@ +package com.baeldung.security.jwt; + +import com.baeldung.security.AuthoritiesConstants; +import io.github.jhipster.config.JHipsterProperties; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; + +import static org.assertj.core.api.Assertions.assertThat; + +public class TokenProviderTest { + + private final String secretKey = "e5c9ee274ae87bc031adda32e27fa98b9290da83"; + private final long ONE_MINUTE = 60000; + private JHipsterProperties jHipsterProperties; + private TokenProvider tokenProvider; + + @Before + public void setup() { + jHipsterProperties = Mockito.mock(JHipsterProperties.class); + tokenProvider = new TokenProvider(jHipsterProperties); + ReflectionTestUtils.setField(tokenProvider, "secretKey", secretKey); + ReflectionTestUtils.setField(tokenProvider, "tokenValidityInMilliseconds", ONE_MINUTE); + } + + @Test + public void testReturnFalseWhenJWThasInvalidSignature() { + boolean isTokenValid = tokenProvider.validateToken(createTokenWithDifferentSignature()); + + assertThat(isTokenValid).isEqualTo(false); + } + + @Test + public void testReturnFalseWhenJWTisMalformed() { + Authentication authentication = createAuthentication(); + String token = tokenProvider.createToken(authentication, false); + String invalidToken = token.substring(1); + boolean isTokenValid = tokenProvider.validateToken(invalidToken); + + assertThat(isTokenValid).isEqualTo(false); + } + + @Test + public void testReturnFalseWhenJWTisExpired() { + ReflectionTestUtils.setField(tokenProvider, "tokenValidityInMilliseconds", -ONE_MINUTE); + + Authentication authentication = createAuthentication(); + String token = tokenProvider.createToken(authentication, false); + + boolean isTokenValid = tokenProvider.validateToken(token); + + assertThat(isTokenValid).isEqualTo(false); + } + + @Test + public void testReturnFalseWhenJWTisUnsupported() { + Date expirationDate = new Date(new Date().getTime() + ONE_MINUTE); + + Authentication authentication = createAuthentication(); + + String unsupportedToken = createUnsupportedToken(); + + boolean isTokenValid = tokenProvider.validateToken(unsupportedToken); + + assertThat(isTokenValid).isEqualTo(false); + } + + @Test + public void testReturnFalseWhenJWTisInvalid() { + + boolean isTokenValid = tokenProvider.validateToken(""); + + assertThat(isTokenValid).isEqualTo(false); + } + + private Authentication createAuthentication() { + Collection authorities = new ArrayList<>(); + authorities.add(new SimpleGrantedAuthority(AuthoritiesConstants.ANONYMOUS)); + return new UsernamePasswordAuthenticationToken("anonymous", "anonymous", authorities); + } + + private String createUnsupportedToken() { + return Jwts.builder() + .setPayload("payload") + .signWith(SignatureAlgorithm.HS512, secretKey) + .compact(); + } + + private String createTokenWithDifferentSignature() { + return Jwts.builder() + .setSubject("anonymous") + .signWith(SignatureAlgorithm.HS512, "e5c9ee274ae87bc031adda32e27fa98b9290da90") + .setExpiration(new Date(new Date().getTime() + ONE_MINUTE)) + .compact(); + } +} diff --git a/jhipster/src/test/java/com/baeldung/service/UserServiceIntTest.java b/jhipster/src/test/java/com/baeldung/service/UserServiceIntTest.java new file mode 100644 index 000000000000..968f0a7f08ac --- /dev/null +++ b/jhipster/src/test/java/com/baeldung/service/UserServiceIntTest.java @@ -0,0 +1,129 @@ +package com.baeldung.service; + +import com.baeldung.BaeldungApp; +import com.baeldung.domain.User; +import com.baeldung.config.Constants; +import com.baeldung.repository.UserRepository; +import com.baeldung.service.dto.UserDTO; +import java.time.ZonedDateTime; +import com.baeldung.service.util.RandomUtil; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.test.context.junit4.SpringRunner; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import java.util.Optional; +import java.util.List; + +import static org.assertj.core.api.Assertions.*; + +/** + * Test class for the UserResource REST controller. + * + * @see UserService + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = BaeldungApp.class) +@Transactional +public class UserServiceIntTest { + + @Autowired + private UserRepository userRepository; + + @Autowired + private UserService userService; + + @Test + public void assertThatUserMustExistToResetPassword() { + Optional maybeUser = userService.requestPasswordReset("john.doe@localhost"); + assertThat(maybeUser.isPresent()).isFalse(); + + maybeUser = userService.requestPasswordReset("admin@localhost"); + assertThat(maybeUser.isPresent()).isTrue(); + + assertThat(maybeUser.get().getEmail()).isEqualTo("admin@localhost"); + assertThat(maybeUser.get().getResetDate()).isNotNull(); + assertThat(maybeUser.get().getResetKey()).isNotNull(); + } + + @Test + public void assertThatOnlyActivatedUserCanRequestPasswordReset() { + User user = userService.createUser("johndoe", "johndoe", "John", "Doe", "john.doe@localhost", "http://placehold.it/50x50", "en-US"); + Optional maybeUser = userService.requestPasswordReset("john.doe@localhost"); + assertThat(maybeUser.isPresent()).isFalse(); + userRepository.delete(user); + } + + @Test + public void assertThatResetKeyMustNotBeOlderThan24Hours() { + User user = userService.createUser("johndoe", "johndoe", "John", "Doe", "john.doe@localhost", "http://placehold.it/50x50", "en-US"); + + ZonedDateTime daysAgo = ZonedDateTime.now().minusHours(25); + String resetKey = RandomUtil.generateResetKey(); + user.setActivated(true); + user.setResetDate(daysAgo); + user.setResetKey(resetKey); + + userRepository.save(user); + + Optional maybeUser = userService.completePasswordReset("johndoe2", user.getResetKey()); + + assertThat(maybeUser.isPresent()).isFalse(); + + userRepository.delete(user); + } + + @Test + public void assertThatResetKeyMustBeValid() { + User user = userService.createUser("johndoe", "johndoe", "John", "Doe", "john.doe@localhost", "http://placehold.it/50x50", "en-US"); + + ZonedDateTime daysAgo = ZonedDateTime.now().minusHours(25); + user.setActivated(true); + user.setResetDate(daysAgo); + user.setResetKey("1234"); + userRepository.save(user); + Optional maybeUser = userService.completePasswordReset("johndoe2", user.getResetKey()); + assertThat(maybeUser.isPresent()).isFalse(); + userRepository.delete(user); + } + + @Test + public void assertThatUserCanResetPassword() { + User user = userService.createUser("johndoe", "johndoe", "John", "Doe", "john.doe@localhost", "http://placehold.it/50x50", "en-US"); + String oldPassword = user.getPassword(); + ZonedDateTime daysAgo = ZonedDateTime.now().minusHours(2); + String resetKey = RandomUtil.generateResetKey(); + user.setActivated(true); + user.setResetDate(daysAgo); + user.setResetKey(resetKey); + userRepository.save(user); + Optional maybeUser = userService.completePasswordReset("johndoe2", user.getResetKey()); + assertThat(maybeUser.isPresent()).isTrue(); + assertThat(maybeUser.get().getResetDate()).isNull(); + assertThat(maybeUser.get().getResetKey()).isNull(); + assertThat(maybeUser.get().getPassword()).isNotEqualTo(oldPassword); + + userRepository.delete(user); + } + + @Test + public void testFindNotActivatedUsersByCreationDateBefore() { + userService.removeNotActivatedUsers(); + ZonedDateTime now = ZonedDateTime.now(); + List users = userRepository.findAllByActivatedIsFalseAndCreatedDateBefore(now.minusDays(3)); + assertThat(users).isEmpty(); + } + + @Test + public void assertThatAnonymousUserIsNotGet() { + final PageRequest pageable = new PageRequest(0, (int) userRepository.count()); + final Page allManagedUsers = userService.getAllManagedUsers(pageable); + assertThat(allManagedUsers.getContent().stream() + .noneMatch(user -> Constants.ANONYMOUS_USER.equals(user.getLogin()))) + .isTrue(); + } +} diff --git a/jhipster/src/test/java/com/baeldung/web/rest/AccountResourceIntTest.java b/jhipster/src/test/java/com/baeldung/web/rest/AccountResourceIntTest.java new file mode 100644 index 000000000000..e42ce1c6d481 --- /dev/null +++ b/jhipster/src/test/java/com/baeldung/web/rest/AccountResourceIntTest.java @@ -0,0 +1,395 @@ +package com.baeldung.web.rest; + +import com.baeldung.BaeldungApp; +import com.baeldung.domain.Authority; +import com.baeldung.domain.User; +import com.baeldung.repository.AuthorityRepository; +import com.baeldung.repository.UserRepository; +import com.baeldung.security.AuthoritiesConstants; +import com.baeldung.service.MailService; +import com.baeldung.service.UserService; +import com.baeldung.service.dto.UserDTO; +import com.baeldung.web.rest.vm.ManagedUserVM; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.when; +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.*; + +/** + * Test class for the AccountResource REST controller. + * + * @see AccountResource + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = BaeldungApp.class) +public class AccountResourceIntTest { + + @Autowired + private UserRepository userRepository; + + @Autowired + private AuthorityRepository authorityRepository; + + @Autowired + private UserService userService; + + @Mock + private UserService mockUserService; + + @Mock + private MailService mockMailService; + + private MockMvc restUserMockMvc; + + private MockMvc restMvc; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + doNothing().when(mockMailService).sendActivationEmail(anyObject()); + + AccountResource accountResource = + new AccountResource(userRepository, userService, mockMailService); + + AccountResource accountUserMockResource = + new AccountResource(userRepository, mockUserService, mockMailService); + + this.restMvc = MockMvcBuilders.standaloneSetup(accountResource).build(); + this.restUserMockMvc = MockMvcBuilders.standaloneSetup(accountUserMockResource).build(); + } + + @Test + public void testNonAuthenticatedUser() throws Exception { + restUserMockMvc.perform(get("/api/authenticate") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().string("")); + } + + @Test + public void testAuthenticatedUser() throws Exception { + restUserMockMvc.perform(get("/api/authenticate") + .with(request -> { + request.setRemoteUser("test"); + return request; + }) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().string("test")); + } + + @Test + public void testGetExistingAccount() throws Exception { + Set authorities = new HashSet<>(); + Authority authority = new Authority(); + authority.setName(AuthoritiesConstants.ADMIN); + authorities.add(authority); + + User user = new User(); + user.setLogin("test"); + user.setFirstName("john"); + user.setLastName("doe"); + user.setEmail("john.doe@jhipster.com"); + user.setImageUrl("http://placehold.it/50x50"); + user.setAuthorities(authorities); + when(mockUserService.getUserWithAuthorities()).thenReturn(user); + + restUserMockMvc.perform(get("/api/account") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect(jsonPath("$.login").value("test")) + .andExpect(jsonPath("$.firstName").value("john")) + .andExpect(jsonPath("$.lastName").value("doe")) + .andExpect(jsonPath("$.email").value("john.doe@jhipster.com")) + .andExpect(jsonPath("$.imageUrl").value("http://placehold.it/50x50")) + .andExpect(jsonPath("$.authorities").value(AuthoritiesConstants.ADMIN)); + } + + @Test + public void testGetUnknownAccount() throws Exception { + when(mockUserService.getUserWithAuthorities()).thenReturn(null); + + restUserMockMvc.perform(get("/api/account") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isInternalServerError()); + } + + @Test + @Transactional + public void testRegisterValid() throws Exception { + ManagedUserVM validUser = new ManagedUserVM( + null, // id + "joe", // login + "password", // password + "Joe", // firstName + "Shmoe", // lastName + "joe@example.com", // e-mail + true, // activated + "http://placehold.it/50x50", //imageUrl + "en", // langKey + null, // createdBy + null, // createdDate + null, // lastModifiedBy + null, // lastModifiedDate + new HashSet<>(Arrays.asList(AuthoritiesConstants.USER))); + + restMvc.perform( + post("/api/register") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(validUser))) + .andExpect(status().isCreated()); + + Optional user = userRepository.findOneByLogin("joe"); + assertThat(user.isPresent()).isTrue(); + } + + @Test + @Transactional + public void testRegisterInvalidLogin() throws Exception { + ManagedUserVM invalidUser = new ManagedUserVM( + null, // id + "funky-log!n", // login <-- invalid + "password", // password + "Funky", // firstName + "One", // lastName + "funky@example.com", // e-mail + true, // activated + "http://placehold.it/50x50", //imageUrl + "en", // langKey + null, // createdBy + null, // createdDate + null, // lastModifiedBy + null, // lastModifiedDate + new HashSet<>(Arrays.asList(AuthoritiesConstants.USER))); + + restUserMockMvc.perform( + post("/api/register") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(invalidUser))) + .andExpect(status().isBadRequest()); + + Optional user = userRepository.findOneByEmail("funky@example.com"); + assertThat(user.isPresent()).isFalse(); + } + + @Test + @Transactional + public void testRegisterInvalidEmail() throws Exception { + ManagedUserVM invalidUser = new ManagedUserVM( + null, // id + "bob", // login + "password", // password + "Bob", // firstName + "Green", // lastName + "invalid", // e-mail <-- invalid + true, // activated + "http://placehold.it/50x50", //imageUrl + "en", // langKey + null, // createdBy + null, // createdDate + null, // lastModifiedBy + null, // lastModifiedDate + new HashSet<>(Arrays.asList(AuthoritiesConstants.USER))); + + restUserMockMvc.perform( + post("/api/register") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(invalidUser))) + .andExpect(status().isBadRequest()); + + Optional user = userRepository.findOneByLogin("bob"); + assertThat(user.isPresent()).isFalse(); + } + + @Test + @Transactional + public void testRegisterInvalidPassword() throws Exception { + ManagedUserVM invalidUser = new ManagedUserVM( + null, // id + "bob", // login + "123", // password with only 3 digits + "Bob", // firstName + "Green", // lastName + "bob@example.com", // e-mail + true, // activated + "http://placehold.it/50x50", //imageUrl + "en", // langKey + null, // createdBy + null, // createdDate + null, // lastModifiedBy + null, // lastModifiedDate + new HashSet<>(Arrays.asList(AuthoritiesConstants.USER))); + + restUserMockMvc.perform( + post("/api/register") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(invalidUser))) + .andExpect(status().isBadRequest()); + + Optional user = userRepository.findOneByLogin("bob"); + assertThat(user.isPresent()).isFalse(); + } + + @Test + @Transactional + public void testRegisterDuplicateLogin() throws Exception { + // Good + ManagedUserVM validUser = new ManagedUserVM( + null, // id + "alice", // login + "password", // password + "Alice", // firstName + "Something", // lastName + "alice@example.com", // e-mail + true, // activated + "http://placehold.it/50x50", //imageUrl + "en", // langKey + null, // createdBy + null, // createdDate + null, // lastModifiedBy + null, // lastModifiedDate + new HashSet<>(Arrays.asList(AuthoritiesConstants.USER))); + + // Duplicate login, different e-mail + ManagedUserVM duplicatedUser = new ManagedUserVM(validUser.getId(), validUser.getLogin(), validUser.getPassword(), validUser.getLogin(), validUser.getLastName(), + "alicejr@example.com", true, validUser.getImageUrl(), validUser.getLangKey(), validUser.getCreatedBy(), validUser.getCreatedDate(), validUser.getLastModifiedBy(), validUser.getLastModifiedDate(), validUser.getAuthorities()); + + // Good user + restMvc.perform( + post("/api/register") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(validUser))) + .andExpect(status().isCreated()); + + // Duplicate login + restMvc.perform( + post("/api/register") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(duplicatedUser))) + .andExpect(status().is4xxClientError()); + + Optional userDup = userRepository.findOneByEmail("alicejr@example.com"); + assertThat(userDup.isPresent()).isFalse(); + } + + @Test + @Transactional + public void testRegisterDuplicateEmail() throws Exception { + // Good + ManagedUserVM validUser = new ManagedUserVM( + null, // id + "john", // login + "password", // password + "John", // firstName + "Doe", // lastName + "john@example.com", // e-mail + true, // activated + "http://placehold.it/50x50", //imageUrl + "en", // langKey + null, // createdBy + null, // createdDate + null, // lastModifiedBy + null, // lastModifiedDate + new HashSet<>(Arrays.asList(AuthoritiesConstants.USER))); + + // Duplicate e-mail, different login + ManagedUserVM duplicatedUser = new ManagedUserVM(validUser.getId(), "johnjr", validUser.getPassword(), validUser.getLogin(), validUser.getLastName(), + validUser.getEmail(), true, validUser.getImageUrl(), validUser.getLangKey(), validUser.getCreatedBy(), validUser.getCreatedDate(), validUser.getLastModifiedBy(), validUser.getLastModifiedDate(), validUser.getAuthorities()); + + // Good user + restMvc.perform( + post("/api/register") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(validUser))) + .andExpect(status().isCreated()); + + // Duplicate e-mail + restMvc.perform( + post("/api/register") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(duplicatedUser))) + .andExpect(status().is4xxClientError()); + + Optional userDup = userRepository.findOneByLogin("johnjr"); + assertThat(userDup.isPresent()).isFalse(); + } + + @Test + @Transactional + public void testRegisterAdminIsIgnored() throws Exception { + ManagedUserVM validUser = new ManagedUserVM( + null, // id + "badguy", // login + "password", // password + "Bad", // firstName + "Guy", // lastName + "badguy@example.com", // e-mail + true, // activated + "http://placehold.it/50x50", //imageUrl + "en", // langKey + null, // createdBy + null, // createdDate + null, // lastModifiedBy + null, // lastModifiedDate + new HashSet<>(Arrays.asList(AuthoritiesConstants.ADMIN))); + + restMvc.perform( + post("/api/register") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(validUser))) + .andExpect(status().isCreated()); + + Optional userDup = userRepository.findOneByLogin("badguy"); + assertThat(userDup.isPresent()).isTrue(); + assertThat(userDup.get().getAuthorities()).hasSize(1) + .containsExactly(authorityRepository.findOne(AuthoritiesConstants.USER)); + } + + @Test + @Transactional + public void testSaveInvalidLogin() throws Exception { + UserDTO invalidUser = new UserDTO( + null, // id + "funky-log!n", // login <-- invalid + "Funky", // firstName + "One", // lastName + "funky@example.com", // e-mail + true, // activated + "http://placehold.it/50x50", //imageUrl + "en", // langKey + null, // createdBy + null, // createdDate + null, // lastModifiedBy + null, // lastModifiedDate + new HashSet<>(Arrays.asList(AuthoritiesConstants.USER)) + ); + + restUserMockMvc.perform( + post("/api/account") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(invalidUser))) + .andExpect(status().isBadRequest()); + + Optional user = userRepository.findOneByEmail("funky@example.com"); + assertThat(user.isPresent()).isFalse(); + } +} diff --git a/jhipster/src/test/java/com/baeldung/web/rest/AuditResourceIntTest.java b/jhipster/src/test/java/com/baeldung/web/rest/AuditResourceIntTest.java new file mode 100644 index 000000000000..127cb36f0755 --- /dev/null +++ b/jhipster/src/test/java/com/baeldung/web/rest/AuditResourceIntTest.java @@ -0,0 +1,147 @@ +package com.baeldung.web.rest; + +import com.baeldung.BaeldungApp; +import com.baeldung.config.audit.AuditEventConverter; +import com.baeldung.domain.PersistentAuditEvent; +import com.baeldung.repository.PersistenceAuditEventRepository; +import com.baeldung.service.AuditEventService; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.web.PageableHandlerMethodArgumentResolver; +import org.springframework.format.support.FormattingConversionService; +import org.springframework.http.MediaType; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +import static org.hamcrest.Matchers.hasItem; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +/** + * Test class for the AuditResource REST controller. + * + * @see AuditResource + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = BaeldungApp.class) +@Transactional +public class AuditResourceIntTest { + + private static final String SAMPLE_PRINCIPAL = "SAMPLE_PRINCIPAL"; + private static final String SAMPLE_TYPE = "SAMPLE_TYPE"; + private static final LocalDateTime SAMPLE_TIMESTAMP = LocalDateTime.parse("2015-08-04T10:11:30"); + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + @Autowired + private PersistenceAuditEventRepository auditEventRepository; + + @Autowired + private AuditEventConverter auditEventConverter; + + @Autowired + private MappingJackson2HttpMessageConverter jacksonMessageConverter; + + @Autowired + private FormattingConversionService formattingConversionService; + + @Autowired + private PageableHandlerMethodArgumentResolver pageableArgumentResolver; + + private PersistentAuditEvent auditEvent; + + private MockMvc restAuditMockMvc; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + AuditEventService auditEventService = + new AuditEventService(auditEventRepository, auditEventConverter); + AuditResource auditResource = new AuditResource(auditEventService); + this.restAuditMockMvc = MockMvcBuilders.standaloneSetup(auditResource) + .setCustomArgumentResolvers(pageableArgumentResolver) + .setConversionService(formattingConversionService) + .setMessageConverters(jacksonMessageConverter).build(); + } + + @Before + public void initTest() { + auditEventRepository.deleteAll(); + auditEvent = new PersistentAuditEvent(); + auditEvent.setAuditEventType(SAMPLE_TYPE); + auditEvent.setPrincipal(SAMPLE_PRINCIPAL); + auditEvent.setAuditEventDate(SAMPLE_TIMESTAMP); + } + + @Test + public void getAllAudits() throws Exception { + // Initialize the database + auditEventRepository.save(auditEvent); + + // Get all the audits + restAuditMockMvc.perform(get("/management/audits")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect(jsonPath("$.[*].principal").value(hasItem(SAMPLE_PRINCIPAL))); + } + + @Test + public void getAudit() throws Exception { + // Initialize the database + auditEventRepository.save(auditEvent); + + // Get the audit + restAuditMockMvc.perform(get("/management/audits/{id}", auditEvent.getId())) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect(jsonPath("$.principal").value(SAMPLE_PRINCIPAL)); + } + + @Test + public void getAuditsByDate() throws Exception { + // Initialize the database + auditEventRepository.save(auditEvent); + + // Generate dates for selecting audits by date, making sure the period will contain the audit + String fromDate = SAMPLE_TIMESTAMP.minusDays(1).format(FORMATTER); + String toDate = SAMPLE_TIMESTAMP.plusDays(1).format(FORMATTER); + + // Get the audit + restAuditMockMvc.perform(get("/management/audits?fromDate="+fromDate+"&toDate="+toDate)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect(jsonPath("$.[*].principal").value(hasItem(SAMPLE_PRINCIPAL))); + } + + @Test + public void getNonExistingAuditsByDate() throws Exception { + // Initialize the database + auditEventRepository.save(auditEvent); + + // Generate dates for selecting audits by date, making sure the period will not contain the sample audit + String fromDate = SAMPLE_TIMESTAMP.minusDays(2).format(FORMATTER); + String toDate = SAMPLE_TIMESTAMP.minusDays(1).format(FORMATTER); + + // Query audits but expect no results + restAuditMockMvc.perform(get("/management/audits?fromDate=" + fromDate + "&toDate=" + toDate)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect(header().string("X-Total-Count", "0")); + } + + @Test + public void getNonExistingAudit() throws Exception { + // Get the audit + restAuditMockMvc.perform(get("/management/audits/{id}", Long.MAX_VALUE)) + .andExpect(status().isNotFound()); + } +} diff --git a/jhipster/src/test/java/com/baeldung/web/rest/CommentResourceIntTest.java b/jhipster/src/test/java/com/baeldung/web/rest/CommentResourceIntTest.java new file mode 100644 index 000000000000..04b16b25f879 --- /dev/null +++ b/jhipster/src/test/java/com/baeldung/web/rest/CommentResourceIntTest.java @@ -0,0 +1,279 @@ +package com.baeldung.web.rest; + +import com.baeldung.BaeldungApp; + +import com.baeldung.domain.Comment; +import com.baeldung.domain.Post; +import com.baeldung.repository.CommentRepository; +import com.baeldung.web.rest.errors.ExceptionTranslator; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.web.PageableHandlerMethodArgumentResolver; +import org.springframework.http.MediaType; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.transaction.annotation.Transactional; + +import javax.persistence.EntityManager; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.hasItem; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +/** + * Test class for the CommentResource REST controller. + * + * @see CommentResource + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = BaeldungApp.class) +public class CommentResourceIntTest { + + private static final String DEFAULT_TEXT = "AAAAAAAAAA"; + private static final String UPDATED_TEXT = "BBBBBBBBBB"; + + private static final LocalDate DEFAULT_CREATION_DATE = LocalDate.ofEpochDay(0L); + private static final LocalDate UPDATED_CREATION_DATE = LocalDate.now(ZoneId.systemDefault()); + + @Autowired + private CommentRepository commentRepository; + + @Autowired + private MappingJackson2HttpMessageConverter jacksonMessageConverter; + + @Autowired + private PageableHandlerMethodArgumentResolver pageableArgumentResolver; + + @Autowired + private ExceptionTranslator exceptionTranslator; + + @Autowired + private EntityManager em; + + private MockMvc restCommentMockMvc; + + private Comment comment; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + CommentResource commentResource = new CommentResource(commentRepository); + this.restCommentMockMvc = MockMvcBuilders.standaloneSetup(commentResource) + .setCustomArgumentResolvers(pageableArgumentResolver) + .setControllerAdvice(exceptionTranslator) + .setMessageConverters(jacksonMessageConverter).build(); + } + + /** + * Create an entity for this test. + * + * This is a static method, as tests for other entities might also need it, + * if they test an entity which requires the current entity. + */ + public static Comment createEntity(EntityManager em) { + Comment comment = new Comment() + .text(DEFAULT_TEXT) + .creationDate(DEFAULT_CREATION_DATE); + // Add required entity + Post post = PostResourceIntTest.createEntity(em); + em.persist(post); + em.flush(); + comment.setPost(post); + return comment; + } + + @Before + public void initTest() { + comment = createEntity(em); + } + + @Test + @Transactional + public void createComment() throws Exception { + int databaseSizeBeforeCreate = commentRepository.findAll().size(); + + // Create the Comment + restCommentMockMvc.perform(post("/api/comments") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(comment))) + .andExpect(status().isCreated()); + + // Validate the Comment in the database + List commentList = commentRepository.findAll(); + assertThat(commentList).hasSize(databaseSizeBeforeCreate + 1); + Comment testComment = commentList.get(commentList.size() - 1); + assertThat(testComment.getText()).isEqualTo(DEFAULT_TEXT); + assertThat(testComment.getCreationDate()).isEqualTo(DEFAULT_CREATION_DATE); + } + + @Test + @Transactional + public void createCommentWithExistingId() throws Exception { + int databaseSizeBeforeCreate = commentRepository.findAll().size(); + + // Create the Comment with an existing ID + comment.setId(1L); + + // An entity with an existing ID cannot be created, so this API call must fail + restCommentMockMvc.perform(post("/api/comments") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(comment))) + .andExpect(status().isBadRequest()); + + // Validate the Alice in the database + List commentList = commentRepository.findAll(); + assertThat(commentList).hasSize(databaseSizeBeforeCreate); + } + + @Test + @Transactional + public void checkTextIsRequired() throws Exception { + int databaseSizeBeforeTest = commentRepository.findAll().size(); + // set the field null + comment.setText(null); + + // Create the Comment, which fails. + + restCommentMockMvc.perform(post("/api/comments") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(comment))) + .andExpect(status().isBadRequest()); + + List commentList = commentRepository.findAll(); + assertThat(commentList).hasSize(databaseSizeBeforeTest); + } + + @Test + @Transactional + public void checkCreationDateIsRequired() throws Exception { + int databaseSizeBeforeTest = commentRepository.findAll().size(); + // set the field null + comment.setCreationDate(null); + + // Create the Comment, which fails. + + restCommentMockMvc.perform(post("/api/comments") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(comment))) + .andExpect(status().isBadRequest()); + + List commentList = commentRepository.findAll(); + assertThat(commentList).hasSize(databaseSizeBeforeTest); + } + + @Test + @Transactional + public void getAllComments() throws Exception { + // Initialize the database + commentRepository.saveAndFlush(comment); + + // Get all the commentList + restCommentMockMvc.perform(get("/api/comments?sort=id,desc")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect(jsonPath("$.[*].id").value(hasItem(comment.getId().intValue()))) + .andExpect(jsonPath("$.[*].text").value(hasItem(DEFAULT_TEXT.toString()))) + .andExpect(jsonPath("$.[*].creationDate").value(hasItem(DEFAULT_CREATION_DATE.toString()))); + } + + @Test + @Transactional + public void getComment() throws Exception { + // Initialize the database + commentRepository.saveAndFlush(comment); + + // Get the comment + restCommentMockMvc.perform(get("/api/comments/{id}", comment.getId())) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect(jsonPath("$.id").value(comment.getId().intValue())) + .andExpect(jsonPath("$.text").value(DEFAULT_TEXT.toString())) + .andExpect(jsonPath("$.creationDate").value(DEFAULT_CREATION_DATE.toString())); + } + + @Test + @Transactional + public void getNonExistingComment() throws Exception { + // Get the comment + restCommentMockMvc.perform(get("/api/comments/{id}", Long.MAX_VALUE)) + .andExpect(status().isNotFound()); + } + + @Test + @Transactional + public void updateComment() throws Exception { + // Initialize the database + commentRepository.saveAndFlush(comment); + int databaseSizeBeforeUpdate = commentRepository.findAll().size(); + + // Update the comment + Comment updatedComment = commentRepository.findOne(comment.getId()); + updatedComment + .text(UPDATED_TEXT) + .creationDate(UPDATED_CREATION_DATE); + + restCommentMockMvc.perform(put("/api/comments") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(updatedComment))) + .andExpect(status().isOk()); + + // Validate the Comment in the database + List commentList = commentRepository.findAll(); + assertThat(commentList).hasSize(databaseSizeBeforeUpdate); + Comment testComment = commentList.get(commentList.size() - 1); + assertThat(testComment.getText()).isEqualTo(UPDATED_TEXT); + assertThat(testComment.getCreationDate()).isEqualTo(UPDATED_CREATION_DATE); + } + + @Test + @Transactional + public void updateNonExistingComment() throws Exception { + int databaseSizeBeforeUpdate = commentRepository.findAll().size(); + + // Create the Comment + + // If the entity doesn't have an ID, it will be created instead of just being updated + restCommentMockMvc.perform(put("/api/comments") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(comment))) + .andExpect(status().isCreated()); + + // Validate the Comment in the database + List commentList = commentRepository.findAll(); + assertThat(commentList).hasSize(databaseSizeBeforeUpdate + 1); + } + + @Test + @Transactional + public void deleteComment() throws Exception { + // Initialize the database + commentRepository.saveAndFlush(comment); + int databaseSizeBeforeDelete = commentRepository.findAll().size(); + + // Get the comment + restCommentMockMvc.perform(delete("/api/comments/{id}", comment.getId()) + .accept(TestUtil.APPLICATION_JSON_UTF8)) + .andExpect(status().isOk()); + + // Validate the database is empty + List commentList = commentRepository.findAll(); + assertThat(commentList).hasSize(databaseSizeBeforeDelete - 1); + } + + @Test + @Transactional + public void equalsVerifier() throws Exception { + TestUtil.equalsVerifier(Comment.class); + } +} diff --git a/jhipster/src/test/java/com/baeldung/web/rest/LogsResourceIntTest.java b/jhipster/src/test/java/com/baeldung/web/rest/LogsResourceIntTest.java new file mode 100644 index 000000000000..92bb976205cb --- /dev/null +++ b/jhipster/src/test/java/com/baeldung/web/rest/LogsResourceIntTest.java @@ -0,0 +1,59 @@ +package com.baeldung.web.rest; + +import com.baeldung.BaeldungApp; +import com.baeldung.web.rest.vm.LoggerVM; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Test class for the LogsResource REST controller. + * + * @see LogsResource + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = BaeldungApp.class) +public class LogsResourceIntTest { + + private MockMvc restLogsMockMvc; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + + LogsResource logsResource = new LogsResource(); + this.restLogsMockMvc = MockMvcBuilders + .standaloneSetup(logsResource) + .build(); + } + + @Test + public void getAllLogs()throws Exception { + restLogsMockMvc.perform(get("/management/logs")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)); + } + + @Test + public void changeLogs()throws Exception { + LoggerVM logger = new LoggerVM(); + logger.setLevel("INFO"); + logger.setName("ROOT"); + + restLogsMockMvc.perform(put("/management/logs") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(logger))) + .andExpect(status().isNoContent()); + } +} diff --git a/jhipster/src/test/java/com/baeldung/web/rest/PostResourceIntTest.java b/jhipster/src/test/java/com/baeldung/web/rest/PostResourceIntTest.java new file mode 100644 index 000000000000..2a23452711c4 --- /dev/null +++ b/jhipster/src/test/java/com/baeldung/web/rest/PostResourceIntTest.java @@ -0,0 +1,306 @@ +package com.baeldung.web.rest; + +import com.baeldung.BaeldungApp; + +import com.baeldung.domain.Post; +import com.baeldung.domain.User; +import com.baeldung.repository.PostRepository; +import com.baeldung.web.rest.errors.ExceptionTranslator; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.web.PageableHandlerMethodArgumentResolver; +import org.springframework.http.MediaType; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.transaction.annotation.Transactional; + +import javax.persistence.EntityManager; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.hasItem; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +/** + * Test class for the PostResource REST controller. + * + * @see PostResource + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = BaeldungApp.class) +public class PostResourceIntTest { + + private static final String DEFAULT_TITLE = "AAAAAAAAAA"; + private static final String UPDATED_TITLE = "BBBBBBBBBB"; + + private static final String DEFAULT_CONTENT = "AAAAAAAAAA"; + private static final String UPDATED_CONTENT = "BBBBBBBBBB"; + + private static final LocalDate DEFAULT_CREATION_DATE = LocalDate.ofEpochDay(0L); + private static final LocalDate UPDATED_CREATION_DATE = LocalDate.now(ZoneId.systemDefault()); + + @Autowired + private PostRepository postRepository; + + @Autowired + private MappingJackson2HttpMessageConverter jacksonMessageConverter; + + @Autowired + private PageableHandlerMethodArgumentResolver pageableArgumentResolver; + + @Autowired + private ExceptionTranslator exceptionTranslator; + + @Autowired + private EntityManager em; + + private MockMvc restPostMockMvc; + + private Post post; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + PostResource postResource = new PostResource(postRepository); + this.restPostMockMvc = MockMvcBuilders.standaloneSetup(postResource) + .setCustomArgumentResolvers(pageableArgumentResolver) + .setControllerAdvice(exceptionTranslator) + .setMessageConverters(jacksonMessageConverter).build(); + } + + /** + * Create an entity for this test. + * + * This is a static method, as tests for other entities might also need it, + * if they test an entity which requires the current entity. + */ + public static Post createEntity(EntityManager em) { + Post post = new Post() + .title(DEFAULT_TITLE) + .content(DEFAULT_CONTENT) + .creationDate(DEFAULT_CREATION_DATE); + // Add required entity + User creator = UserResourceIntTest.createEntity(em); + em.persist(creator); + em.flush(); + post.setCreator(creator); + return post; + } + + @Before + public void initTest() { + post = createEntity(em); + } + + @Test + @Transactional + public void createPost() throws Exception { + int databaseSizeBeforeCreate = postRepository.findAll().size(); + + // Create the Post + restPostMockMvc.perform(post("/api/posts") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(post))) + .andExpect(status().isCreated()); + + // Validate the Post in the database + List postList = postRepository.findAll(); + assertThat(postList).hasSize(databaseSizeBeforeCreate + 1); + Post testPost = postList.get(postList.size() - 1); + assertThat(testPost.getTitle()).isEqualTo(DEFAULT_TITLE); + assertThat(testPost.getContent()).isEqualTo(DEFAULT_CONTENT); + assertThat(testPost.getCreationDate()).isEqualTo(DEFAULT_CREATION_DATE); + } + + @Test + @Transactional + public void createPostWithExistingId() throws Exception { + int databaseSizeBeforeCreate = postRepository.findAll().size(); + + // Create the Post with an existing ID + post.setId(1L); + + // An entity with an existing ID cannot be created, so this API call must fail + restPostMockMvc.perform(post("/api/posts") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(post))) + .andExpect(status().isBadRequest()); + + // Validate the Alice in the database + List postList = postRepository.findAll(); + assertThat(postList).hasSize(databaseSizeBeforeCreate); + } + + @Test + @Transactional + public void checkTitleIsRequired() throws Exception { + int databaseSizeBeforeTest = postRepository.findAll().size(); + // set the field null + post.setTitle(null); + + // Create the Post, which fails. + + restPostMockMvc.perform(post("/api/posts") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(post))) + .andExpect(status().isBadRequest()); + + List postList = postRepository.findAll(); + assertThat(postList).hasSize(databaseSizeBeforeTest); + } + + @Test + @Transactional + public void checkContentIsRequired() throws Exception { + int databaseSizeBeforeTest = postRepository.findAll().size(); + // set the field null + post.setContent(null); + + // Create the Post, which fails. + + restPostMockMvc.perform(post("/api/posts") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(post))) + .andExpect(status().isBadRequest()); + + List postList = postRepository.findAll(); + assertThat(postList).hasSize(databaseSizeBeforeTest); + } + + @Test + @Transactional + public void checkCreationDateIsRequired() throws Exception { + int databaseSizeBeforeTest = postRepository.findAll().size(); + // set the field null + post.setCreationDate(null); + + // Create the Post, which fails. + + restPostMockMvc.perform(post("/api/posts") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(post))) + .andExpect(status().isBadRequest()); + + List postList = postRepository.findAll(); + assertThat(postList).hasSize(databaseSizeBeforeTest); + } + + @Test + @Transactional + public void getAllPosts() throws Exception { + // Initialize the database + postRepository.saveAndFlush(post); + + // Get all the postList + restPostMockMvc.perform(get("/api/posts?sort=id,desc")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect(jsonPath("$.[*].id").value(hasItem(post.getId().intValue()))) + .andExpect(jsonPath("$.[*].title").value(hasItem(DEFAULT_TITLE.toString()))) + .andExpect(jsonPath("$.[*].content").value(hasItem(DEFAULT_CONTENT.toString()))) + .andExpect(jsonPath("$.[*].creationDate").value(hasItem(DEFAULT_CREATION_DATE.toString()))); + } + + @Test + @Transactional + public void getPost() throws Exception { + // Initialize the database + postRepository.saveAndFlush(post); + + // Get the post + restPostMockMvc.perform(get("/api/posts/{id}", post.getId())) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect(jsonPath("$.id").value(post.getId().intValue())) + .andExpect(jsonPath("$.title").value(DEFAULT_TITLE.toString())) + .andExpect(jsonPath("$.content").value(DEFAULT_CONTENT.toString())) + .andExpect(jsonPath("$.creationDate").value(DEFAULT_CREATION_DATE.toString())); + } + + @Test + @Transactional + public void getNonExistingPost() throws Exception { + // Get the post + restPostMockMvc.perform(get("/api/posts/{id}", Long.MAX_VALUE)) + .andExpect(status().isNotFound()); + } + + @Test + @Transactional + public void updatePost() throws Exception { + // Initialize the database + postRepository.saveAndFlush(post); + int databaseSizeBeforeUpdate = postRepository.findAll().size(); + + // Update the post + Post updatedPost = postRepository.findOne(post.getId()); + updatedPost + .title(UPDATED_TITLE) + .content(UPDATED_CONTENT) + .creationDate(UPDATED_CREATION_DATE); + + restPostMockMvc.perform(put("/api/posts") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(updatedPost))) + .andExpect(status().isOk()); + + // Validate the Post in the database + List postList = postRepository.findAll(); + assertThat(postList).hasSize(databaseSizeBeforeUpdate); + Post testPost = postList.get(postList.size() - 1); + assertThat(testPost.getTitle()).isEqualTo(UPDATED_TITLE); + assertThat(testPost.getContent()).isEqualTo(UPDATED_CONTENT); + assertThat(testPost.getCreationDate()).isEqualTo(UPDATED_CREATION_DATE); + } + + @Test + @Transactional + public void updateNonExistingPost() throws Exception { + int databaseSizeBeforeUpdate = postRepository.findAll().size(); + + // Create the Post + + // If the entity doesn't have an ID, it will be created instead of just being updated + restPostMockMvc.perform(put("/api/posts") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(post))) + .andExpect(status().isCreated()); + + // Validate the Post in the database + List postList = postRepository.findAll(); + assertThat(postList).hasSize(databaseSizeBeforeUpdate + 1); + } + + @Test + @Transactional + public void deletePost() throws Exception { + // Initialize the database + postRepository.saveAndFlush(post); + int databaseSizeBeforeDelete = postRepository.findAll().size(); + + // Get the post + restPostMockMvc.perform(delete("/api/posts/{id}", post.getId()) + .accept(TestUtil.APPLICATION_JSON_UTF8)) + .andExpect(status().isOk()); + + // Validate the database is empty + List postList = postRepository.findAll(); + assertThat(postList).hasSize(databaseSizeBeforeDelete - 1); + } + + @Test + @Transactional + public void equalsVerifier() throws Exception { + TestUtil.equalsVerifier(Post.class); + } +} diff --git a/jhipster/src/test/java/com/baeldung/web/rest/ProfileInfoResourceIntTest.java b/jhipster/src/test/java/com/baeldung/web/rest/ProfileInfoResourceIntTest.java new file mode 100644 index 000000000000..df3544f34421 --- /dev/null +++ b/jhipster/src/test/java/com/baeldung/web/rest/ProfileInfoResourceIntTest.java @@ -0,0 +1,86 @@ +package com.baeldung.web.rest; + +import io.github.jhipster.config.JHipsterProperties; +import com.baeldung.BaeldungApp; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.core.env.Environment; +import org.springframework.http.MediaType; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Test class for the ProfileInfoResource REST controller. + * + * @see ProfileInfoResource + **/ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = BaeldungApp.class) +public class ProfileInfoResourceIntTest { + + @Mock + private Environment environment; + + @Mock + private JHipsterProperties jHipsterProperties; + + private MockMvc restProfileMockMvc; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + String mockProfile[] = {"test"}; + JHipsterProperties.Ribbon ribbon = new JHipsterProperties.Ribbon(); + ribbon.setDisplayOnActiveProfiles(mockProfile); + when(jHipsterProperties.getRibbon()).thenReturn(ribbon); + + String activeProfiles[] = {"test"}; + when(environment.getDefaultProfiles()).thenReturn(activeProfiles); + when(environment.getActiveProfiles()).thenReturn(activeProfiles); + + ProfileInfoResource profileInfoResource = new ProfileInfoResource(environment, jHipsterProperties); + this.restProfileMockMvc = MockMvcBuilders + .standaloneSetup(profileInfoResource) + .build(); + } + + @Test + public void getProfileInfoWithRibbon() throws Exception { + restProfileMockMvc.perform(get("/api/profile-info")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)); + } + + @Test + public void getProfileInfoWithoutRibbon() throws Exception { + JHipsterProperties.Ribbon ribbon = new JHipsterProperties.Ribbon(); + ribbon.setDisplayOnActiveProfiles(null); + when(jHipsterProperties.getRibbon()).thenReturn(ribbon); + + restProfileMockMvc.perform(get("/api/profile-info")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)); + } + + @Test + public void getProfileInfoWithoutActiveProfiles() throws Exception { + String emptyProfile[] = {}; + when(environment.getDefaultProfiles()).thenReturn(emptyProfile); + when(environment.getActiveProfiles()).thenReturn(emptyProfile); + + restProfileMockMvc.perform(get("/api/profile-info")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)); + } +} diff --git a/jhipster/src/test/java/com/baeldung/web/rest/TestUtil.java b/jhipster/src/test/java/com/baeldung/web/rest/TestUtil.java new file mode 100644 index 000000000000..64d092fdf175 --- /dev/null +++ b/jhipster/src/test/java/com/baeldung/web/rest/TestUtil.java @@ -0,0 +1,120 @@ +package com.baeldung.web.rest; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import org.hamcrest.Description; +import org.hamcrest.TypeSafeDiagnosingMatcher; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.http.MediaType; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeParseException; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Utility class for testing REST controllers. + */ +public class TestUtil { + + /** MediaType for JSON UTF8 */ + public static final MediaType APPLICATION_JSON_UTF8 = new MediaType( + MediaType.APPLICATION_JSON.getType(), + MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8")); + + /** + * Convert an object to JSON byte array. + * + * @param object + * the object to convert + * @return the JSON byte array + * @throws IOException + */ + public static byte[] convertObjectToJsonBytes(Object object) + throws IOException { + ObjectMapper mapper = new ObjectMapper(); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + + JavaTimeModule module = new JavaTimeModule(); + mapper.registerModule(module); + + return mapper.writeValueAsBytes(object); + } + + /** + * Create a byte array with a specific size filled with specified data. + * + * @param size the size of the byte array + * @param data the data to put in the byte array + * @return the JSON byte array + */ + public static byte[] createByteArray(int size, String data) { + byte[] byteArray = new byte[size]; + for (int i = 0; i < size; i++) { + byteArray[i] = Byte.parseByte(data, 2); + } + return byteArray; + } + + /** + * A matcher that tests that the examined string represents the same instant as the reference datetime. + */ + public static class ZonedDateTimeMatcher extends TypeSafeDiagnosingMatcher { + + private final ZonedDateTime date; + + public ZonedDateTimeMatcher(ZonedDateTime date) { + this.date = date; + } + + @Override + protected boolean matchesSafely(String item, Description mismatchDescription) { + try { + if (!date.isEqual(ZonedDateTime.parse(item))) { + mismatchDescription.appendText("was ").appendValue(item); + return false; + } + return true; + } catch (DateTimeParseException e) { + mismatchDescription.appendText("was ").appendValue(item) + .appendText(", which could not be parsed as a ZonedDateTime"); + return false; + } + + } + + @Override + public void describeTo(Description description) { + description.appendText("a String representing the same Instant as ").appendValue(date); + } + } + + /** + * Creates a matcher that matches when the examined string reprensents the same instant as the reference datetime + * @param date the reference datetime against which the examined string is checked + */ + public static ZonedDateTimeMatcher sameInstant(ZonedDateTime date) { + return new ZonedDateTimeMatcher(date); + } + + /** + * Verifies the equals/hashcode contract on the domain object. + */ + public static void equalsVerifier(Class clazz) throws Exception { + Object domainObject1 = clazz.getConstructor().newInstance(); + assertThat(domainObject1.toString()).isNotNull(); + assertThat(domainObject1).isEqualTo(domainObject1); + assertThat(domainObject1.hashCode()).isEqualTo(domainObject1.hashCode()); + // Test with an instance of another class + Object testOtherObject = new Object(); + assertThat(domainObject1).isNotEqualTo(testOtherObject); + // Test with an instance of the same class + Object domainObject2 = clazz.getConstructor().newInstance(); + assertThat(domainObject1).isNotEqualTo(domainObject2); + // HashCodes are equals because the objects are not persisted yet + assertThat(domainObject1.hashCode()).isEqualTo(domainObject2.hashCode()); + } +} diff --git a/jhipster/src/test/java/com/baeldung/web/rest/UserResourceIntTest.java b/jhipster/src/test/java/com/baeldung/web/rest/UserResourceIntTest.java new file mode 100644 index 000000000000..74df23283aab --- /dev/null +++ b/jhipster/src/test/java/com/baeldung/web/rest/UserResourceIntTest.java @@ -0,0 +1,522 @@ +package com.baeldung.web.rest; + +import com.baeldung.BaeldungApp; +import com.baeldung.domain.User; +import com.baeldung.repository.UserRepository; +import com.baeldung.service.MailService; +import com.baeldung.service.UserService; +import com.baeldung.web.rest.errors.ExceptionTranslator; +import com.baeldung.web.rest.vm.ManagedUserVM; +import org.apache.commons.lang3.RandomStringUtils; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.web.PageableHandlerMethodArgumentResolver; +import org.springframework.http.MediaType; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.transaction.annotation.Transactional; + +import javax.persistence.EntityManager; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.hasItem; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +/** + * Test class for the UserResource REST controller. + * + * @see UserResource + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = BaeldungApp.class) +public class UserResourceIntTest { + + private static final String DEFAULT_LOGIN = "johndoe"; + private static final String UPDATED_LOGIN = "jhipster"; + + private static final String DEFAULT_PASSWORD = "passjohndoe"; + private static final String UPDATED_PASSWORD = "passjhipster"; + + private static final String DEFAULT_EMAIL = "johndoe@localhost"; + private static final String UPDATED_EMAIL = "jhipster@localhost"; + + private static final String DEFAULT_FIRSTNAME = "john"; + private static final String UPDATED_FIRSTNAME = "jhipsterFirstName"; + + private static final String DEFAULT_LASTNAME = "doe"; + private static final String UPDATED_LASTNAME = "jhipsterLastName"; + + private static final String DEFAULT_IMAGEURL = "http://placehold.it/50x50"; + private static final String UPDATED_IMAGEURL = "http://placehold.it/40x40"; + + private static final String DEFAULT_LANGKEY = "en"; + private static final String UPDATED_LANGKEY = "fr"; + + @Autowired + private UserRepository userRepository; + + @Autowired + private MailService mailService; + + @Autowired + private UserService userService; + + @Autowired + private MappingJackson2HttpMessageConverter jacksonMessageConverter; + + @Autowired + private PageableHandlerMethodArgumentResolver pageableArgumentResolver; + + @Autowired + private ExceptionTranslator exceptionTranslator; + + @Autowired + private EntityManager em; + + private MockMvc restUserMockMvc; + + private User user; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + UserResource userResource = new UserResource(userRepository, mailService, userService); + this.restUserMockMvc = MockMvcBuilders.standaloneSetup(userResource) + .setCustomArgumentResolvers(pageableArgumentResolver) + .setControllerAdvice(exceptionTranslator) + .setMessageConverters(jacksonMessageConverter) + .build(); + } + + /** + * Create a User. + * + * This is a static method, as tests for other entities might also need it, + * if they test an entity which has a required relationship to the User entity. + */ + public static User createEntity(EntityManager em) { + User user = new User(); + user.setLogin(DEFAULT_LOGIN); + user.setPassword(RandomStringUtils.random(60)); + user.setActivated(true); + user.setEmail(DEFAULT_EMAIL); + user.setFirstName(DEFAULT_FIRSTNAME); + user.setLastName(DEFAULT_LASTNAME); + user.setImageUrl(DEFAULT_IMAGEURL); + user.setLangKey(DEFAULT_LANGKEY); + return user; + } + + @Before + public void initTest() { + user = createEntity(em); + } + + @Test + @Transactional + public void createUser() throws Exception { + int databaseSizeBeforeCreate = userRepository.findAll().size(); + + // Create the User + Set autorities = new HashSet<>(); + autorities.add("ROLE_USER"); + ManagedUserVM managedUserVM = new ManagedUserVM( + null, + DEFAULT_LOGIN, + DEFAULT_PASSWORD, + DEFAULT_FIRSTNAME, + DEFAULT_LASTNAME, + DEFAULT_EMAIL, + true, + DEFAULT_IMAGEURL, + DEFAULT_LANGKEY, + null, + null, + null, + null, + autorities); + + restUserMockMvc.perform(post("/api/users") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(managedUserVM))) + .andExpect(status().isCreated()); + + // Validate the User in the database + List userList = userRepository.findAll(); + assertThat(userList).hasSize(databaseSizeBeforeCreate + 1); + User testUser = userList.get(userList.size() - 1); + assertThat(testUser.getLogin()).isEqualTo(DEFAULT_LOGIN); + assertThat(testUser.getFirstName()).isEqualTo(DEFAULT_FIRSTNAME); + assertThat(testUser.getLastName()).isEqualTo(DEFAULT_LASTNAME); + assertThat(testUser.getEmail()).isEqualTo(DEFAULT_EMAIL); + assertThat(testUser.getImageUrl()).isEqualTo(DEFAULT_IMAGEURL); + assertThat(testUser.getLangKey()).isEqualTo(DEFAULT_LANGKEY); + } + + @Test + @Transactional + public void createUserWithExistingId() throws Exception { + int databaseSizeBeforeCreate = userRepository.findAll().size(); + + Set autorities = new HashSet<>(); + autorities.add("ROLE_USER"); + ManagedUserVM managedUserVM = new ManagedUserVM( + 1L, + DEFAULT_LOGIN, + DEFAULT_PASSWORD, + DEFAULT_FIRSTNAME, + DEFAULT_LASTNAME, + DEFAULT_EMAIL, + true, + DEFAULT_IMAGEURL, + DEFAULT_LANGKEY, + null, + null, + null, + null, + autorities); + + // An entity with an existing ID cannot be created, so this API call must fail + restUserMockMvc.perform(post("/api/users") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(managedUserVM))) + .andExpect(status().isBadRequest()); + + // Validate the User in the database + List userList = userRepository.findAll(); + assertThat(userList).hasSize(databaseSizeBeforeCreate); + } + + @Test + @Transactional + public void createUserWithExistingLogin() throws Exception { + // Initialize the database + userRepository.saveAndFlush(user); + int databaseSizeBeforeCreate = userRepository.findAll().size(); + + Set autorities = new HashSet<>(); + autorities.add("ROLE_USER"); + ManagedUserVM managedUserVM = new ManagedUserVM( + null, + DEFAULT_LOGIN, // this login should already be used + DEFAULT_PASSWORD, + DEFAULT_FIRSTNAME, + DEFAULT_LASTNAME, + "anothermail@localhost", + true, + DEFAULT_IMAGEURL, + DEFAULT_LANGKEY, + null, + null, + null, + null, + autorities); + + // Create the User + restUserMockMvc.perform(post("/api/users") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(managedUserVM))) + .andExpect(status().isBadRequest()); + + // Validate the User in the database + List userList = userRepository.findAll(); + assertThat(userList).hasSize(databaseSizeBeforeCreate); + } + + @Test + @Transactional + public void createUserWithExistingEmail() throws Exception { + // Initialize the database + userRepository.saveAndFlush(user); + int databaseSizeBeforeCreate = userRepository.findAll().size(); + + Set autorities = new HashSet<>(); + autorities.add("ROLE_USER"); + ManagedUserVM managedUserVM = new ManagedUserVM( + null, + "anotherlogin", + DEFAULT_PASSWORD, + DEFAULT_FIRSTNAME, + DEFAULT_LASTNAME, + DEFAULT_EMAIL, // this email should already be used + true, + DEFAULT_IMAGEURL, + DEFAULT_LANGKEY, + null, + null, + null, + null, + autorities); + + // Create the User + restUserMockMvc.perform(post("/api/users") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(managedUserVM))) + .andExpect(status().isBadRequest()); + + // Validate the User in the database + List userList = userRepository.findAll(); + assertThat(userList).hasSize(databaseSizeBeforeCreate); + } + + @Test + @Transactional + public void getAllUsers() throws Exception { + // Initialize the database + userRepository.saveAndFlush(user); + + // Get all the users + restUserMockMvc.perform(get("/api/users?sort=id,desc") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect(jsonPath("$.[*].login").value(hasItem(DEFAULT_LOGIN))) + .andExpect(jsonPath("$.[*].firstName").value(hasItem(DEFAULT_FIRSTNAME))) + .andExpect(jsonPath("$.[*].lastName").value(hasItem(DEFAULT_LASTNAME))) + .andExpect(jsonPath("$.[*].email").value(hasItem(DEFAULT_EMAIL))) + .andExpect(jsonPath("$.[*].imageUrl").value(hasItem(DEFAULT_IMAGEURL))) + .andExpect(jsonPath("$.[*].langKey").value(hasItem(DEFAULT_LANGKEY))); + } + + @Test + @Transactional + public void getUser() throws Exception { + // Initialize the database + userRepository.saveAndFlush(user); + + // Get the user + restUserMockMvc.perform(get("/api/users/{login}", user.getLogin())) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect(jsonPath("$.login").value(user.getLogin())) + .andExpect(jsonPath("$.firstName").value(DEFAULT_FIRSTNAME)) + .andExpect(jsonPath("$.lastName").value(DEFAULT_LASTNAME)) + .andExpect(jsonPath("$.email").value(DEFAULT_EMAIL)) + .andExpect(jsonPath("$.imageUrl").value(DEFAULT_IMAGEURL)) + .andExpect(jsonPath("$.langKey").value(DEFAULT_LANGKEY)); + } + + @Test + @Transactional + public void getNonExistingUser() throws Exception { + restUserMockMvc.perform(get("/api/users/unknown")) + .andExpect(status().isNotFound()); + } + + @Test + @Transactional + public void updateUser() throws Exception { + // Initialize the database + userRepository.saveAndFlush(user); + int databaseSizeBeforeUpdate = userRepository.findAll().size(); + + // Update the user + User updatedUser = userRepository.findOne(user.getId()); + + Set autorities = new HashSet<>(); + autorities.add("ROLE_USER"); + ManagedUserVM managedUserVM = new ManagedUserVM( + updatedUser.getId(), + updatedUser.getLogin(), + UPDATED_PASSWORD, + UPDATED_FIRSTNAME, + UPDATED_LASTNAME, + UPDATED_EMAIL, + updatedUser.getActivated(), + UPDATED_IMAGEURL, + UPDATED_LANGKEY, + updatedUser.getCreatedBy(), + updatedUser.getCreatedDate(), + updatedUser.getLastModifiedBy(), + updatedUser.getLastModifiedDate(), + autorities); + + restUserMockMvc.perform(put("/api/users") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(managedUserVM))) + .andExpect(status().isOk()); + + // Validate the User in the database + List userList = userRepository.findAll(); + assertThat(userList).hasSize(databaseSizeBeforeUpdate); + User testUser = userList.get(userList.size() - 1); + assertThat(testUser.getFirstName()).isEqualTo(UPDATED_FIRSTNAME); + assertThat(testUser.getLastName()).isEqualTo(UPDATED_LASTNAME); + assertThat(testUser.getEmail()).isEqualTo(UPDATED_EMAIL); + assertThat(testUser.getImageUrl()).isEqualTo(UPDATED_IMAGEURL); + assertThat(testUser.getLangKey()).isEqualTo(UPDATED_LANGKEY); + } + + @Test + @Transactional + public void updateUserLogin() throws Exception { + // Initialize the database + userRepository.saveAndFlush(user); + int databaseSizeBeforeUpdate = userRepository.findAll().size(); + + // Update the user + User updatedUser = userRepository.findOne(user.getId()); + + Set autorities = new HashSet<>(); + autorities.add("ROLE_USER"); + ManagedUserVM managedUserVM = new ManagedUserVM( + updatedUser.getId(), + UPDATED_LOGIN, + UPDATED_PASSWORD, + UPDATED_FIRSTNAME, + UPDATED_LASTNAME, + UPDATED_EMAIL, + updatedUser.getActivated(), + UPDATED_IMAGEURL, + UPDATED_LANGKEY, + updatedUser.getCreatedBy(), + updatedUser.getCreatedDate(), + updatedUser.getLastModifiedBy(), + updatedUser.getLastModifiedDate(), + autorities); + + restUserMockMvc.perform(put("/api/users") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(managedUserVM))) + .andExpect(status().isOk()); + + // Validate the User in the database + List userList = userRepository.findAll(); + assertThat(userList).hasSize(databaseSizeBeforeUpdate); + User testUser = userList.get(userList.size() - 1); + assertThat(testUser.getLogin()).isEqualTo(UPDATED_LOGIN); + assertThat(testUser.getFirstName()).isEqualTo(UPDATED_FIRSTNAME); + assertThat(testUser.getLastName()).isEqualTo(UPDATED_LASTNAME); + assertThat(testUser.getEmail()).isEqualTo(UPDATED_EMAIL); + assertThat(testUser.getImageUrl()).isEqualTo(UPDATED_IMAGEURL); + assertThat(testUser.getLangKey()).isEqualTo(UPDATED_LANGKEY); + } + + @Test + @Transactional + public void updateUserExistingEmail() throws Exception { + // Initialize the database with 2 users + userRepository.saveAndFlush(user); + + User anotherUser = new User(); + anotherUser.setLogin("jhipster"); + anotherUser.setPassword(RandomStringUtils.random(60)); + anotherUser.setActivated(true); + anotherUser.setEmail("jhipster@localhost"); + anotherUser.setFirstName("java"); + anotherUser.setLastName("hipster"); + anotherUser.setImageUrl(""); + anotherUser.setLangKey("en"); + userRepository.saveAndFlush(anotherUser); + + int databaseSizeBeforeUpdate = userRepository.findAll().size(); + + // Update the user + User updatedUser = userRepository.findOne(user.getId()); + + Set autorities = new HashSet<>(); + autorities.add("ROLE_USER"); + ManagedUserVM managedUserVM = new ManagedUserVM( + updatedUser.getId(), + updatedUser.getLogin(), + updatedUser.getPassword(), + updatedUser.getFirstName(), + updatedUser.getLastName(), + "jhipster@localhost", // this email should already be used by anotherUser + updatedUser.getActivated(), + updatedUser.getImageUrl(), + updatedUser.getLangKey(), + updatedUser.getCreatedBy(), + updatedUser.getCreatedDate(), + updatedUser.getLastModifiedBy(), + updatedUser.getLastModifiedDate(), + autorities); + + restUserMockMvc.perform(put("/api/users") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(managedUserVM))) + .andExpect(status().isBadRequest()); + } + + @Test + @Transactional + public void updateUserExistingLogin() throws Exception { + // Initialize the database + userRepository.saveAndFlush(user); + + User anotherUser = new User(); + anotherUser.setLogin("jhipster"); + anotherUser.setPassword(RandomStringUtils.random(60)); + anotherUser.setActivated(true); + anotherUser.setEmail("jhipster@localhost"); + anotherUser.setFirstName("java"); + anotherUser.setLastName("hipster"); + anotherUser.setImageUrl(""); + anotherUser.setLangKey("en"); + userRepository.saveAndFlush(anotherUser); + int databaseSizeBeforeUpdate = userRepository.findAll().size(); + + // Update the user + User updatedUser = userRepository.findOne(user.getId()); + + Set autorities = new HashSet<>(); + autorities.add("ROLE_USER"); + ManagedUserVM managedUserVM = new ManagedUserVM( + updatedUser.getId(), + "jhipster", // this login should already be used by anotherUser + updatedUser.getPassword(), + updatedUser.getFirstName(), + updatedUser.getLastName(), + updatedUser.getEmail(), + updatedUser.getActivated(), + updatedUser.getImageUrl(), + updatedUser.getLangKey(), + updatedUser.getCreatedBy(), + updatedUser.getCreatedDate(), + updatedUser.getLastModifiedBy(), + updatedUser.getLastModifiedDate(), + autorities); + + restUserMockMvc.perform(put("/api/users") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(managedUserVM))) + .andExpect(status().isBadRequest()); + } + + @Test + @Transactional + public void deleteUser() throws Exception { + // Initialize the database + userRepository.saveAndFlush(user); + int databaseSizeBeforeDelete = userRepository.findAll().size(); + + // Delete the user + restUserMockMvc.perform(delete("/api/users/{login}", user.getLogin()) + .accept(TestUtil.APPLICATION_JSON_UTF8)) + .andExpect(status().isOk()); + + // Validate the database is empty + List userList = userRepository.findAll(); + assertThat(userList).hasSize(databaseSizeBeforeDelete - 1); + } + + @Test + @Transactional + public void equalsVerifier() throws Exception { + User userA = new User(); + userA.setLogin("AAA"); + User userB = new User(); + userB.setLogin("BBB"); + assertThat(userA).isNotEqualTo(userB); + } +} diff --git a/jhipster/src/test/javascript/e2e/account/account.spec.ts b/jhipster/src/test/javascript/e2e/account/account.spec.ts new file mode 100644 index 000000000000..cfa9fae0786e --- /dev/null +++ b/jhipster/src/test/javascript/e2e/account/account.spec.ts @@ -0,0 +1,108 @@ +import { browser, element, by, $ } from 'protractor'; + +describe('account', () => { + + const username = element(by.id('username')); + const password = element(by.id('password')); + const accountMenu = element(by.id('account-menu')); + const login = element(by.id('login')); + const logout = element(by.id('logout')); + + beforeAll(() => { + browser.get('/'); + }); + + it('should fail to login with bad password', () => { + const expect1 = /home.title/; + element.all(by.css('h1')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expect1); + }); + accountMenu.click(); + login.click(); + + username.sendKeys('admin'); + password.sendKeys('foo'); + element(by.css('button[type=submit]')).click(); + + const expect2 = /login.messages.error.authentication/; + element.all(by.css('.alert-danger')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expect2); + }); + }); + + it('should login successfully with admin account', () => { + const expect1 = /login.title/; + element.all(by.css('.modal-content h1')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expect1); + }); + username.clear(); + username.sendKeys('admin'); + password.clear(); + password.sendKeys('admin'); + element(by.css('button[type=submit]')).click(); + + browser.waitForAngular(); + + const expect2 = /home.logged.message/; + element.all(by.css('.alert-success span')).getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expect2); + }); + }); + + it('should be able to update settings', () => { + accountMenu.click(); + element(by.css('[routerLink="settings"]')).click(); + + const expect1 = /settings.title/; + element.all(by.css('h2')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expect1); + }); + element(by.css('button[type=submit]')).click(); + + const expect2 = /settings.messages.success/; + element.all(by.css('.alert-success')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expect2); + }); + }); + + it('should be able to update password', () => { + accountMenu.click(); + element(by.css('[routerLink="password"]')).click(); + + const expect1 = /password.title/; + element.all(by.css('h2')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expect1); + }); + password.sendKeys('newpassword'); + element(by.id('confirmPassword')).sendKeys('newpassword'); + element(by.css('button[type=submit]')).click(); + + const expect2 = /password.messages.success/; + element.all(by.css('.alert-success')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expect2); + }); + accountMenu.click(); + logout.click(); + + accountMenu.click(); + login.click(); + + username.sendKeys('admin'); + password.sendKeys('newpassword'); + element(by.css('button[type=submit]')).click(); + + accountMenu.click(); + element(by.css('[routerLink="password"]')).click(); + // change back to default + password.clear(); + password.sendKeys('admin'); + element(by.id('confirmPassword')).clear(); + element(by.id('confirmPassword')).sendKeys('admin'); + element(by.css('button[type=submit]')).click(); + }); + + afterAll(() => { + accountMenu.click(); + logout.click(); + }); +}); diff --git a/jhipster/src/test/javascript/e2e/admin/administration.spec.ts b/jhipster/src/test/javascript/e2e/admin/administration.spec.ts new file mode 100644 index 000000000000..0516f7a27d96 --- /dev/null +++ b/jhipster/src/test/javascript/e2e/admin/administration.spec.ts @@ -0,0 +1,80 @@ +import { browser, element, by, $ } from 'protractor'; + +describe('administration', () => { + + const username = element(by.id('username')); + const password = element(by.id('password')); + const accountMenu = element(by.id('account-menu')); + const adminMenu = element(by.id('admin-menu')); + const login = element(by.id('login')); + const logout = element(by.id('logout')); + + beforeAll(() => { + browser.get('/'); + + accountMenu.click(); + login.click(); + + username.sendKeys('admin'); + password.sendKeys('admin'); + element(by.css('button[type=submit]')).click(); + browser.waitForAngular(); + }); + + beforeEach(() => { + adminMenu.click(); + }); + + it('should load user management', () => { + element(by.css('[routerLink="user-management"]')).click(); + const expect1 = /userManagement.home.title/; + element.all(by.css('h2 span')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expect1); + }); + }); + + it('should load metrics', () => { + element(by.css('[routerLink="jhi-metrics"]')).click(); + const expect1 = /metrics.title/; + element.all(by.css('h2 span')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expect1); + }); + }); + + it('should load health', () => { + element(by.css('[routerLink="jhi-health"]')).click(); + const expect1 = /health.title/; + element.all(by.css('h2 span')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expect1); + }); + }); + + it('should load configuration', () => { + element(by.css('[routerLink="jhi-configuration"]')).click(); + const expect1 = /configuration.title/; + element.all(by.css('h2')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expect1); + }); + }); + + it('should load audits', () => { + element(by.css('[routerLink="audits"]')).click(); + const expect1 = /audits.title/; + element.all(by.css('h2')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expect1); + }); + }); + + it('should load logs', () => { + element(by.css('[routerLink="logs"]')).click(); + const expect1 = /logs.title/; + element.all(by.css('h2')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expect1); + }); + }); + + afterAll(() => { + accountMenu.click(); + logout.click(); + }); +}); diff --git a/jhipster/src/test/javascript/e2e/entities/comment.spec.ts b/jhipster/src/test/javascript/e2e/entities/comment.spec.ts new file mode 100644 index 000000000000..3032bd136495 --- /dev/null +++ b/jhipster/src/test/javascript/e2e/entities/comment.spec.ts @@ -0,0 +1,49 @@ +import { browser, element, by, $ } from 'protractor'; + +describe('Comment e2e test', () => { + + const username = element(by.id('username')); + const password = element(by.id('password')); + const entityMenu = element(by.id('entity-menu')); + const accountMenu = element(by.id('account-menu')); + const login = element(by.id('login')); + const logout = element(by.id('logout')); + + beforeAll(() => { + browser.get('/'); + + accountMenu.click(); + login.click(); + + username.sendKeys('admin'); + password.sendKeys('admin'); + element(by.css('button[type=submit]')).click(); + browser.waitForAngular(); + }); + + it('should load Comments', () => { + entityMenu.click(); + element.all(by.css('[routerLink="comment"]')).first().click().then(() => { + const expectVal = /baeldungApp.comment.home.title/; + element.all(by.css('h2 span')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expectVal); + }); + }); + }); + + it('should load create Comment dialog', function () { + element(by.css('button.create-comment')).click().then(() => { + const expectVal = /baeldungApp.comment.home.createOrEditLabel/; + element.all(by.css('h4.modal-title')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expectVal); + }); + + element(by.css('button.close')).click(); + }); + }); + + afterAll(function () { + accountMenu.click(); + logout.click(); + }); +}); diff --git a/jhipster/src/test/javascript/e2e/entities/post.spec.ts b/jhipster/src/test/javascript/e2e/entities/post.spec.ts new file mode 100644 index 000000000000..3c8d04f7316d --- /dev/null +++ b/jhipster/src/test/javascript/e2e/entities/post.spec.ts @@ -0,0 +1,49 @@ +import { browser, element, by, $ } from 'protractor'; + +describe('Post e2e test', () => { + + const username = element(by.id('username')); + const password = element(by.id('password')); + const entityMenu = element(by.id('entity-menu')); + const accountMenu = element(by.id('account-menu')); + const login = element(by.id('login')); + const logout = element(by.id('logout')); + + beforeAll(() => { + browser.get('/'); + + accountMenu.click(); + login.click(); + + username.sendKeys('admin'); + password.sendKeys('admin'); + element(by.css('button[type=submit]')).click(); + browser.waitForAngular(); + }); + + it('should load Posts', () => { + entityMenu.click(); + element.all(by.css('[routerLink="post"]')).first().click().then(() => { + const expectVal = /baeldungApp.post.home.title/; + element.all(by.css('h2 span')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expectVal); + }); + }); + }); + + it('should load create Post dialog', function () { + element(by.css('button.create-post')).click().then(() => { + const expectVal = /baeldungApp.post.home.createOrEditLabel/; + element.all(by.css('h4.modal-title')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expectVal); + }); + + element(by.css('button.close')).click(); + }); + }); + + afterAll(function () { + accountMenu.click(); + logout.click(); + }); +}); diff --git a/jhipster/src/test/javascript/karma.conf.js b/jhipster/src/test/javascript/karma.conf.js new file mode 100644 index 000000000000..1b1022695503 --- /dev/null +++ b/jhipster/src/test/javascript/karma.conf.js @@ -0,0 +1,126 @@ +'use strict'; + +const path = require('path'); +const webpack = require('webpack'); +const WATCH = process.argv.indexOf('--watch') > -1; +const LoaderOptionsPlugin = require("webpack/lib/LoaderOptionsPlugin"); + +module.exports = function (config) { + config.set({ + + // base path that will be used to resolve all patterns (eg. files, exclude) + basePath: './', + + // frameworks to use + // available frameworks: https://npmjs.org/browse/keyword/karma-adapter + frameworks: ['jasmine', 'intl-shim'], + + // list of files / patterns to load in the browser + files: [ + 'spec/entry.ts' + ], + + + // list of files to exclude + exclude: ['e2e/**'], + + // preprocess matching files before serving them to the browser + // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor + preprocessors: { + 'spec/entry.ts': ['webpack', 'sourcemap'] + }, + + webpack: { + resolve: { + extensions: ['.ts', '.js'] + }, + module: { + rules: [ + { + test: /\.ts$/, enforce: 'pre', loader: 'tslint-loader', exclude: /(test|node_modules)/ + }, + { + test: /\.ts$/, + loaders: ['awesome-typescript-loader', 'angular2-template-loader?keepUrl=true'], + exclude: /node_modules/ + }, + { + test: /\.(html|css)$/, + loader: 'raw-loader', + exclude: /\.async\.(html|css)$/ + }, + { + test: /\.async\.(html|css)$/, + loaders: ['file?name=[name].[hash].[ext]', 'extract'] + }, + { + test: /\.scss$/, + loaders: ['to-string-loader', 'css-loader', 'sass-loader'] + }, + { + test: /src\/main\/webapp\/.+\.ts$/, + enforce: 'post', + exclude: /(test|node_modules)/, + loader: 'sourcemap-istanbul-instrumenter-loader?force-sourcemap=true' + }] + }, + devtool: 'inline-source-map', + plugins: [ + new webpack.ContextReplacementPlugin( + // The (\\|\/) piece accounts for path separators in *nix and Windows + /angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/, + root('./src') // location of your src + ), + new LoaderOptionsPlugin({ + options: { + tslint: { + emitErrors: !WATCH, + failOnHint: false + } + } + }) + ] + }, + + // test results reporter to use + // possible values: 'dots', 'progress' + // available reporters: https://npmjs.org/browse/keyword/karma-reporter + reporters: ['dots', 'junit', 'progress', 'karma-remap-istanbul'], + + junitReporter: { + outputFile: '../../../../target/test-results/karma/TESTS-results.xml' + }, + + remapIstanbulReporter: { + reports: { // eslint-disable-line + 'html': 'target/test-results/coverage', + 'text-summary': null + } + }, + + // web server port + port: 9876, + + // enable / disable colors in the output (reporters and logs) + colors: true, + + // level of logging + // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG + logLevel: config.LOG_INFO, + + // enable / disable watching file and executing tests whenever any file changes + autoWatch: WATCH, + + // start these browsers + // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher + browsers: ['PhantomJS'], + + // Continuous Integration mode + // if true, Karma captures browsers, runs the tests and exits + singleRun: !WATCH + }); +}; + +function root(__path) { + return path.join(__dirname, __path); +} diff --git a/jhipster/src/test/javascript/protractor.conf.js b/jhipster/src/test/javascript/protractor.conf.js new file mode 100644 index 000000000000..e79b09e17bba --- /dev/null +++ b/jhipster/src/test/javascript/protractor.conf.js @@ -0,0 +1,48 @@ +var HtmlScreenshotReporter = require("protractor-jasmine2-screenshot-reporter"); +var JasmineReporters = require('jasmine-reporters'); + +exports.config = { + allScriptsTimeout: 20000, + + specs: [ + './e2e/account/*.spec.ts', + './e2e/admin/*.spec.ts', + './e2e/entities/*.spec.ts' + ], + + capabilities: { + 'browserName': 'chrome', + 'phantomjs.binary.path': require('phantomjs-prebuilt').path, + 'phantomjs.ghostdriver.cli.args': ['--loglevel=DEBUG'] + }, + + directConnect: true, + + baseUrl: 'http://localhost:8080/', + + framework: 'jasmine2', + + jasmineNodeOpts: { + showColors: true, + defaultTimeoutInterval: 30000 + }, + + beforeLaunch: function() { + require('ts-node').register({ + project: '' + }); + }, + + onPrepare: function() { + browser.driver.manage().window().setSize(1280, 1024); + jasmine.getEnv().addReporter(new JasmineReporters.JUnitXmlReporter({ + savePath: 'target/reports/e2e', + consolidateAll: false + })); + jasmine.getEnv().addReporter(new HtmlScreenshotReporter({ + dest: "target/reports/e2e/screenshots" + })); + }, + + useAllAngular2AppRoots: true +}; diff --git a/jhipster/src/test/javascript/spec/app/account/activate/activate.component.spec.ts b/jhipster/src/test/javascript/spec/app/account/activate/activate.component.spec.ts new file mode 100644 index 000000000000..76a6e9f9417f --- /dev/null +++ b/jhipster/src/test/javascript/spec/app/account/activate/activate.component.spec.ts @@ -0,0 +1,84 @@ +import { TestBed, async, tick, fakeAsync, inject } from '@angular/core/testing'; +import { ActivatedRoute } from '@angular/router'; +import { Observable } from 'rxjs/Rx'; +import { BaeldungTestModule } from '../../../test.module'; +import { MockActivatedRoute } from '../../../helpers/mock-route.service'; +import { LoginModalService } from '../../../../../../main/webapp/app/shared'; +import { Activate } from '../../../../../../main/webapp/app/account/activate/activate.service'; +import { ActivateComponent } from '../../../../../../main/webapp/app/account/activate/activate.component'; + +describe('Component Tests', () => { + + describe('ActivateComponent', () => { + + let comp: ActivateComponent; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [BaeldungTestModule], + declarations: [ActivateComponent], + providers: [ + Activate, + { + provide: ActivatedRoute, + useValue: new MockActivatedRoute({'key': 'ABC123'}) + }, + { + provide: LoginModalService, + useValue: null + } + ] + }).overrideComponent(ActivateComponent, { + set: { + template: '' + } + }).compileComponents(); + })); + + beforeEach(() => { + let fixture = TestBed.createComponent(ActivateComponent); + comp = fixture.componentInstance; + }); + + it('calls activate.get with the key from params', + inject([Activate], + fakeAsync((service: Activate) => { + spyOn(service, 'get').and.returnValue(Observable.of()); + + comp.ngOnInit(); + tick(); + + expect(service.get).toHaveBeenCalledWith('ABC123'); + }) + ) + ); + + it('should set set success to OK upon successful activation', + inject([Activate], + fakeAsync((service: Activate) => { + spyOn(service, 'get').and.returnValue(Observable.of({})); + + comp.ngOnInit(); + tick(); + + expect(comp.error).toBe(null); + expect(comp.success).toEqual('OK'); + }) + ) + ); + + it('should set set error to ERROR upon activation failure', + inject([Activate], + fakeAsync((service: Activate) => { + spyOn(service, 'get').and.returnValue(Observable.throw('ERROR')); + + comp.ngOnInit(); + tick(); + + expect(comp.error).toBe('ERROR'); + expect(comp.success).toEqual(null); + }) + ) + ); + }); +}); diff --git a/jhipster/src/test/javascript/spec/app/account/password-reset/finish/password-reset-finish.component.spec.ts b/jhipster/src/test/javascript/spec/app/account/password-reset/finish/password-reset-finish.component.spec.ts new file mode 100644 index 000000000000..537c58351ef9 --- /dev/null +++ b/jhipster/src/test/javascript/spec/app/account/password-reset/finish/password-reset-finish.component.spec.ts @@ -0,0 +1,79 @@ +import { ComponentFixture, TestBed, inject } from '@angular/core/testing'; +import { Renderer, ElementRef } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { LoginModalService } from '../../../../../../../main/webapp/app/shared'; +import { Observable } from 'rxjs/Rx'; +import { BaeldungTestModule } from '../../../../test.module'; +import { PasswordResetFinishComponent } from '../../../../../../../main/webapp/app/account/password-reset/finish/password-reset-finish.component'; +import { PasswordResetFinish } from '../../../../../../../main/webapp/app/account/password-reset/finish/password-reset-finish.service'; +import { MockActivatedRoute } from '../../../../helpers/mock-route.service'; + + +describe('Component Tests', () => { + + describe('PasswordResetFinishComponent', () => { + + let fixture: ComponentFixture; + let comp: PasswordResetFinishComponent; + + beforeEach(() => { + fixture = TestBed.configureTestingModule({ + imports: [BaeldungTestModule], + declarations: [PasswordResetFinishComponent], + providers: [ + PasswordResetFinish, + { + provide: LoginModalService, + useValue: null + }, + { + provide: ActivatedRoute, + useValue: new MockActivatedRoute({'key': 'XYZPDQ'}) + }, + { + provide: Renderer, + useValue: { + invokeElementMethod(renderElement: any, methodName: string, args?: any[]) {} + } + }, + { + provide: ElementRef, + useValue: new ElementRef(null) + } + ] + }).overrideComponent(PasswordResetFinishComponent, { + set: { + template: '' + } + }).createComponent(PasswordResetFinishComponent); + comp = fixture.componentInstance; + }); + + it('should define its initial state', function () { + comp.ngOnInit(); + + expect(comp.keyMissing).toBeFalsy(); + expect(comp.key).toEqual('XYZPDQ'); + expect(comp.resetAccount).toEqual({}); + }); + + it('sets focus after the view has been initialized', + inject([ElementRef], (elementRef: ElementRef) => { + let element = fixture.nativeElement; + let node = { + focus() {} + }; + + elementRef.nativeElement = element; + spyOn(element, 'querySelector').and.returnValue(node); + spyOn(node, 'focus'); + + comp.ngAfterViewInit(); + + expect(element.querySelector).toHaveBeenCalledWith('#password'); + expect(node.focus).toHaveBeenCalled(); + }) + ); + + }); +}); diff --git a/jhipster/src/test/javascript/spec/app/account/password-reset/init/password-reset-init.component.spec.ts b/jhipster/src/test/javascript/spec/app/account/password-reset/init/password-reset-init.component.spec.ts new file mode 100644 index 000000000000..55c0a81922d3 --- /dev/null +++ b/jhipster/src/test/javascript/spec/app/account/password-reset/init/password-reset-init.component.spec.ts @@ -0,0 +1,115 @@ +import { ComponentFixture, TestBed, inject } from '@angular/core/testing'; +import { Renderer, ElementRef } from '@angular/core'; +import { Observable } from 'rxjs/Rx'; +import { BaeldungTestModule } from '../../../../test.module'; +import { PasswordResetInitComponent } from '../../../../../../../main/webapp/app/account/password-reset/init/password-reset-init.component'; +import { PasswordResetInit } from '../../../../../../../main/webapp/app/account/password-reset/init/password-reset-init.service'; + + +describe('Component Tests', () => { + + describe('PasswordResetInitComponent', function () { + let fixture: ComponentFixture; + let comp: PasswordResetInitComponent; + + beforeEach(() => { + fixture = TestBed.configureTestingModule({ + imports: [BaeldungTestModule], + declarations: [PasswordResetInitComponent], + providers: [ + PasswordResetInit, + { + provide: Renderer, + useValue: { + invokeElementMethod(renderElement: any, methodName: string, args?: any[]) {} + } + }, + { + provide: ElementRef, + useValue: new ElementRef(null) + } + ] + }).overrideComponent(PasswordResetInitComponent, { + set: { + template: '' + } + }).createComponent(PasswordResetInitComponent); + comp = fixture.componentInstance; + comp.ngOnInit(); + }); + + it('should define its initial state', function () { + expect(comp.success).toBeUndefined(); + expect(comp.error).toBeUndefined(); + expect(comp.errorEmailNotExists).toBeUndefined(); + expect(comp.resetAccount).toEqual({}); + }); + + it('sets focus after the view has been initialized', + inject([ElementRef], (elementRef: ElementRef) => { + let element = fixture.nativeElement; + let node = { + focus() {} + }; + + elementRef.nativeElement = element; + spyOn(element, 'querySelector').and.returnValue(node); + spyOn(node, 'focus'); + + comp.ngAfterViewInit(); + + expect(element.querySelector).toHaveBeenCalledWith('#email'); + expect(node.focus).toHaveBeenCalled(); + }) + ); + + it('notifies of success upon successful requestReset', + inject([PasswordResetInit], (service: PasswordResetInit) => { + spyOn(service, 'save').and.returnValue(Observable.of({})); + comp.resetAccount.email = 'user@domain.com'; + + comp.requestReset(); + + expect(service.save).toHaveBeenCalledWith('user@domain.com'); + expect(comp.success).toEqual('OK'); + expect(comp.error).toBeNull(); + expect(comp.errorEmailNotExists).toBeNull(); + }) + ); + + it('notifies of unknown email upon e-mail address not registered/400', + inject([PasswordResetInit], (service: PasswordResetInit) => { + spyOn(service, 'save').and.returnValue(Observable.throw({ + status: 400, + data: 'e-mail address not registered' + })); + comp.resetAccount.email = 'user@domain.com'; + + comp.requestReset(); + + expect(service.save).toHaveBeenCalledWith('user@domain.com'); + expect(comp.success).toBeNull(); + expect(comp.error).toBeNull(); + expect(comp.errorEmailNotExists).toEqual('ERROR'); + }) + ); + + it('notifies of error upon error response', + inject([PasswordResetInit], (service: PasswordResetInit) => { + spyOn(service, 'save').and.returnValue(Observable.throw({ + status: 503, + data: 'something else' + })); + comp.resetAccount.email = 'user@domain.com'; + + comp.requestReset(); + + expect(service.save).toHaveBeenCalledWith('user@domain.com'); + expect(comp.success).toBeNull(); + expect(comp.errorEmailNotExists).toBeNull(); + expect(comp.error).toEqual('ERROR'); + }) + ); + + }); +}); diff --git a/jhipster/src/test/javascript/spec/app/account/password/password-strength-bar.component.spec.ts b/jhipster/src/test/javascript/spec/app/account/password/password-strength-bar.component.spec.ts new file mode 100644 index 000000000000..9cdc55529cb3 --- /dev/null +++ b/jhipster/src/test/javascript/spec/app/account/password/password-strength-bar.component.spec.ts @@ -0,0 +1,53 @@ +import { ComponentFixture, TestBed, async } from '@angular/core/testing'; + +import { PasswordStrengthBarComponent } from '../../../../../../main/webapp/app/account/password/password-strength-bar.component'; + +describe('Component Tests', () => { + + describe('PasswordStrengthBarComponent', () => { + + let comp: PasswordStrengthBarComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [PasswordStrengthBarComponent] + }).overrideComponent(PasswordStrengthBarComponent, { + set: { + template: '' + } + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PasswordStrengthBarComponent); + comp = fixture.componentInstance; + }); + + describe('PasswordStrengthBarComponents', () => { + it('should initialize with default values', () => { + expect(comp.measureStrength('')).toBe(0); + expect(comp.colors).toEqual(['#F00', '#F90', '#FF0', '#9F0', '#0F0']); + expect(comp.getColor(0).idx).toBe(1); + expect(comp.getColor(0).col).toBe(comp.colors[0]); + }); + + it('should increase strength upon password value change', () => { + expect(comp.measureStrength('')).toBe(0); + expect(comp.measureStrength('aa')).toBeGreaterThanOrEqual(comp.measureStrength('')); + expect(comp.measureStrength('aa^6')).toBeGreaterThanOrEqual(comp.measureStrength('aa')); + expect(comp.measureStrength('Aa090(**)')).toBeGreaterThanOrEqual(comp.measureStrength('aa^6')); + expect(comp.measureStrength('Aa090(**)+-07365')).toBeGreaterThanOrEqual(comp.measureStrength('Aa090(**)')); + }); + + it('should change the color based on strength', () => { + expect(comp.getColor(0).col).toBe(comp.colors[0]); + expect(comp.getColor(11).col).toBe(comp.colors[1]); + expect(comp.getColor(22).col).toBe(comp.colors[2]); + expect(comp.getColor(33).col).toBe(comp.colors[3]); + expect(comp.getColor(44).col).toBe(comp.colors[4]); + }); + }); + }); +}); + diff --git a/jhipster/src/test/javascript/spec/app/account/password/password.component.spec.ts b/jhipster/src/test/javascript/spec/app/account/password/password.component.spec.ts new file mode 100644 index 000000000000..e6f498378549 --- /dev/null +++ b/jhipster/src/test/javascript/spec/app/account/password/password.component.spec.ts @@ -0,0 +1,92 @@ +import { ComponentFixture, TestBed, async, inject } from '@angular/core/testing'; +import { Observable } from 'rxjs/Rx'; +import { BaeldungTestModule } from '../../../test.module'; +import { PasswordComponent } from '../../../../../../main/webapp/app/account/password/password.component'; +import { Password } from '../../../../../../main/webapp/app/account/password/password.service'; +import { Principal } from '../../../../../../main/webapp/app/shared/auth/principal.service'; +import { AccountService } from '../../../../../../main/webapp/app/shared/auth/account.service'; + + +describe('Component Tests', () => { + + describe('PasswordComponent', () => { + + let comp: PasswordComponent; + let fixture: ComponentFixture; + let service: Password; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [BaeldungTestModule], + declarations: [PasswordComponent], + providers: [ + Principal, + AccountService, + Password + ] + }).overrideComponent(PasswordComponent, { + set: { + template: '' + } + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PasswordComponent); + comp = fixture.componentInstance; + service = fixture.debugElement.injector.get(Password); + }); + + it('should show error if passwords do not match', () => { + // GIVEN + comp.password = 'password1'; + comp.confirmPassword = 'password2'; + // WHEN + comp.changePassword(); + // THEN + expect(comp.doNotMatch).toBe('ERROR'); + expect(comp.error).toBeNull(); + expect(comp.success).toBeNull(); + }); + + it('should call Auth.changePassword when passwords match', () => { + // GIVEN + spyOn(service, 'save').and.returnValue(Observable.of(true)); + comp.password = comp.confirmPassword = 'myPassword'; + + // WHEN + comp.changePassword(); + + // THEN + expect(service.save).toHaveBeenCalledWith('myPassword'); + }); + + it('should set success to OK upon success', function() { + // GIVEN + spyOn(service, 'save').and.returnValue(Observable.of(true)); + comp.password = comp.confirmPassword = 'myPassword'; + + // WHEN + comp.changePassword(); + + // THEN + expect(comp.doNotMatch).toBeNull(); + expect(comp.error).toBeNull(); + expect(comp.success).toBe('OK'); + }); + + it('should notify of error if change password fails', function() { + // GIVEN + spyOn(service, 'save').and.returnValue(Observable.throw('ERROR')); + comp.password = comp.confirmPassword = 'myPassword'; + + // WHEN + comp.changePassword(); + + // THEN + expect(comp.doNotMatch).toBeNull(); + expect(comp.success).toBeNull(); + expect(comp.error).toBe('ERROR'); + }); + }); +}); diff --git a/jhipster/src/test/javascript/spec/app/account/register/register.component.spec.ts b/jhipster/src/test/javascript/spec/app/account/register/register.component.spec.ts new file mode 100644 index 000000000000..c475c2f3d2c9 --- /dev/null +++ b/jhipster/src/test/javascript/spec/app/account/register/register.component.spec.ts @@ -0,0 +1,138 @@ +import { ComponentFixture, TestBed, async, inject, tick, fakeAsync } from '@angular/core/testing'; +import { Renderer, ElementRef } from '@angular/core'; +import { Observable } from 'rxjs/Rx'; +import { JhiLanguageService } from 'ng-jhipster'; +import { MockLanguageService } from '../../../helpers/mock-language.service'; +import { BaeldungTestModule } from '../../../test.module'; +import { LoginModalService } from '../../../../../../main/webapp/app/shared'; +import { Register } from '../../../../../../main/webapp/app/account/register/register.service'; +import { RegisterComponent } from '../../../../../../main/webapp/app/account/register/register.component'; + + +describe('Component Tests', () => { + + describe('RegisterComponent', () => { + let fixture: ComponentFixture; + let comp: RegisterComponent; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [BaeldungTestModule], + declarations: [RegisterComponent], + providers: [ + Register, + { + provide: LoginModalService, + useValue: null + }, + { + provide: Renderer, + useValue: null + }, + { + provide: ElementRef, + useValue: null + } + ] + }).overrideComponent(RegisterComponent, { + set: { + template: '' + } + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(RegisterComponent); + comp = fixture.componentInstance; + comp.ngOnInit(); + }); + + it('should ensure the two passwords entered match', function () { + comp.registerAccount.password = 'password'; + comp.confirmPassword = 'non-matching'; + + comp.register(); + + expect(comp.doNotMatch).toEqual('ERROR'); + }); + + it('should update success to OK after creating an account', + inject([Register, JhiLanguageService], + fakeAsync((service: Register, mockTranslate: MockLanguageService) => { + spyOn(service, 'save').and.returnValue(Observable.of({})); + comp.registerAccount.password = comp.confirmPassword = 'password'; + + comp.register(); + tick(); + + expect(service.save).toHaveBeenCalledWith({ + password: 'password', + langKey: 'en' + }); + expect(comp.success).toEqual(true); + expect(comp.registerAccount.langKey).toEqual('en'); + expect(mockTranslate.getCurrentSpy).toHaveBeenCalled(); + expect(comp.errorUserExists).toBeNull(); + expect(comp.errorEmailExists).toBeNull(); + expect(comp.error).toBeNull(); + }) + ) + ); + + it('should notify of user existence upon 400/login already in use', + inject([Register], + fakeAsync((service: Register) => { + spyOn(service, 'save').and.returnValue(Observable.throw({ + status: 400, + _body: 'login already in use' + })); + comp.registerAccount.password = comp.confirmPassword = 'password'; + + comp.register(); + tick(); + + expect(comp.errorUserExists).toEqual('ERROR'); + expect(comp.errorEmailExists).toBeNull(); + expect(comp.error).toBeNull(); + }) + ) + ); + + it('should notify of email existence upon 400/e-mail address already in use', + inject([Register], + fakeAsync((service: Register) => { + spyOn(service, 'save').and.returnValue(Observable.throw({ + status: 400, + _body: 'e-mail address already in use' + })); + comp.registerAccount.password = comp.confirmPassword = 'password'; + + comp.register(); + tick(); + + expect(comp.errorEmailExists).toEqual('ERROR'); + expect(comp.errorUserExists).toBeNull(); + expect(comp.error).toBeNull(); + }) + ) + ); + + it('should notify of generic error', + inject([Register], + fakeAsync((service: Register) => { + spyOn(service, 'save').and.returnValue(Observable.throw({ + status: 503 + })); + comp.registerAccount.password = comp.confirmPassword = 'password'; + + comp.register(); + tick(); + + expect(comp.errorUserExists).toBeNull(); + expect(comp.errorEmailExists).toBeNull(); + expect(comp.error).toEqual('ERROR'); + }) + ) + ); + }); +}); diff --git a/jhipster/src/test/javascript/spec/app/account/settings/settings.component.spec.ts b/jhipster/src/test/javascript/spec/app/account/settings/settings.component.spec.ts new file mode 100644 index 000000000000..266a33be790d --- /dev/null +++ b/jhipster/src/test/javascript/spec/app/account/settings/settings.component.spec.ts @@ -0,0 +1,103 @@ +import { ComponentFixture, TestBed, async, inject } from '@angular/core/testing'; +import { Observable } from 'rxjs/Rx'; +import { JhiLanguageHelper } from '../../../../../../main/webapp/app/shared'; +import { BaeldungTestModule } from '../../../test.module'; +import { Principal, AccountService } from '../../../../../../main/webapp/app/shared'; +import { SettingsComponent } from '../../../../../../main/webapp/app/account/settings/settings.component'; +import { MockAccountService } from '../../../helpers/mock-account.service'; +import { MockPrincipal } from '../../../helpers/mock-principal.service'; + + +describe('Component Tests', () => { + + describe('SettingsComponent', () => { + + let comp: SettingsComponent; + let fixture: ComponentFixture; + let mockAuth: MockAccountService; + let mockPrincipal: MockPrincipal; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [BaeldungTestModule], + declarations: [SettingsComponent], + providers: [ + { + provide: Principal, + useClass: MockPrincipal + }, + { + provide: AccountService, + useClass: MockAccountService + }, + { + provide: JhiLanguageHelper, + useValue: null + }, + ] + }).overrideComponent(SettingsComponent, { + set: { + template: '' + } + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SettingsComponent); + comp = fixture.componentInstance; + mockAuth = fixture.debugElement.injector.get(AccountService); + mockPrincipal = fixture.debugElement.injector.get(Principal); + }); + + it('should send the current identity upon save', function () { + // GIVEN + let accountValues = { + firstName: 'John', + lastName: 'Doe', + + activated: true, + email: 'john.doe@mail.com', + langKey: 'en', + login: 'john' + }; + mockPrincipal.setResponse(accountValues); + + // WHEN + comp.settingsAccount = accountValues; + comp.save(); + + // THEN + expect(mockPrincipal.identitySpy).toHaveBeenCalled(); + expect(mockAuth.saveSpy).toHaveBeenCalledWith(accountValues); + expect(comp.settingsAccount).toEqual(accountValues); + }); + + it('should notify of success upon successful save', function () { + // GIVEN + let accountValues = { + firstName: 'John', + lastName: 'Doe' + }; + mockPrincipal.setResponse(accountValues); + + // WHEN + comp.save(); + + // THEN + expect(comp.error).toBeNull(); + expect(comp.success).toBe('OK'); + }); + + it('should notify of error upon failed save', function () { + // GIVEN + mockAuth.saveSpy.and.returnValue(Observable.throw('ERROR')); + + // WHEN + comp.save(); + + // THEN + expect(comp.error).toEqual('ERROR'); + expect(comp.success).toBeNull(); + }); + }); +}); diff --git a/jhipster/src/test/javascript/spec/app/admin/audits/audits.component.spec.ts b/jhipster/src/test/javascript/spec/app/admin/audits/audits.component.spec.ts new file mode 100644 index 000000000000..d16673de03db --- /dev/null +++ b/jhipster/src/test/javascript/spec/app/admin/audits/audits.component.spec.ts @@ -0,0 +1,82 @@ +import { ComponentFixture, TestBed, async } from '@angular/core/testing'; +import { DatePipe } from '@angular/common'; +import { NgbPaginationConfig} from '@ng-bootstrap/ng-bootstrap'; +import { ParseLinks } from 'ng-jhipster'; +import { BaeldungTestModule } from '../../../test.module'; +import { PaginationConfig } from '../../../../../../main/webapp/app/blocks/config/uib-pagination.config' +import { AuditsComponent } from '../../../../../../main/webapp/app/admin/audits/audits.component'; +import { AuditsService } from '../../../../../../main/webapp/app/admin/audits/audits.service'; +import { ITEMS_PER_PAGE } from '../../../../../../main/webapp/app/shared'; + + +function getDate(isToday= true){ + let date: Date = new Date(); + if (isToday) { + // Today + 1 day - needed if the current day must be included + date.setDate(date.getDate() + 1); + return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`; + } + return `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`; +} + +describe('Component Tests', () => { + + describe('AuditsComponent', () => { + + let comp: AuditsComponent; + let fixture: ComponentFixture; + let service: AuditsService; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [BaeldungTestModule], + declarations: [AuditsComponent], + providers: [ + AuditsService, + NgbPaginationConfig, + ParseLinks, + PaginationConfig, + DatePipe + ] + }) + .overrideComponent(AuditsComponent, { + set: { + template: '' + } + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AuditsComponent); + comp = fixture.componentInstance; + service = fixture.debugElement.injector.get(AuditsService); + }); + + describe('today function ', () => { + it('should set toDate to current date', () => { + comp.today(); + expect(comp.toDate).toBe(getDate()); + }); + }); + + describe('previousMonth function ', () => { + it('should set toDate to current date', () => { + comp.previousMonth(); + expect(comp.fromDate).toBe(getDate(false)); + }); + }); + + describe('By default, on init', () => { + it('should set all default values correctly', () => { + fixture.detectChanges(); + expect(comp.toDate).toBe(getDate()); + expect(comp.fromDate).toBe(getDate(false)); + expect(comp.itemsPerPage).toBe(ITEMS_PER_PAGE); + expect(comp.page).toBe(1); + expect(comp.reverse).toBeFalsy(); + expect(comp.orderProp).toBe('timestamp'); + }); + }); + }); +}); diff --git a/jhipster/src/test/javascript/spec/app/admin/health/health.component.spec.ts b/jhipster/src/test/javascript/spec/app/admin/health/health.component.spec.ts new file mode 100644 index 000000000000..b80c96db662d --- /dev/null +++ b/jhipster/src/test/javascript/spec/app/admin/health/health.component.spec.ts @@ -0,0 +1,295 @@ +import { ComponentFixture, TestBed, async } from '@angular/core/testing'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { BaeldungTestModule } from '../../../test.module'; +import { JhiHealthCheckComponent } from '../../../../../../main/webapp/app/admin/health/health.component'; +import { JhiHealthService } from '../../../../../../main/webapp/app/admin/health/health.service'; + + +describe('Component Tests', () => { + + describe('JhiHealthCheckComponent', () => { + + let comp: JhiHealthCheckComponent; + let fixture: ComponentFixture; + let service: JhiHealthService; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [BaeldungTestModule], + declarations: [JhiHealthCheckComponent], + providers: [ + JhiHealthService, + { + provide: NgbModal, + useValue: null + } + ] + }) + .overrideComponent(JhiHealthCheckComponent, { + set: { + template: '' + } + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(JhiHealthCheckComponent); + comp = fixture.componentInstance; + service = fixture.debugElement.injector.get(JhiHealthService); + }); + + describe('baseName and subSystemName', () => { + it('should return the basename when it has no sub system', () => { + expect(comp.baseName('base')).toBe('base'); + }); + + it('should return the basename when it has sub systems', () => { + expect(comp.baseName('base.subsystem.system')).toBe('base'); + }); + + it('should return the sub system name', () => { + expect(comp.subSystemName('subsystem')).toBe(''); + }); + + it('should return the subsystem when it has multiple keys', () => { + expect(comp.subSystemName('subsystem.subsystem.system')).toBe(' - subsystem.system'); + }); + }); + + describe('transformHealthData', () => { + it('should flatten empty health data', () => { + const data = {}; + const expected = []; + expect(service.transformHealthData(data)).toEqual(expected); + }); + }); + + it('should flatten health data with no subsystems', () => { + const data = { + 'status': 'UP', + 'db': { + 'status': 'UP', + 'database': 'H2', + 'hello': '1' + }, + 'mail': { + 'status': 'UP', + 'error': 'mail.a.b.c' + } + }; + const expected = [ + { + 'name': 'db', + 'error': undefined, + 'status': 'UP', + 'details': { + 'database': 'H2', + 'hello': '1' + } + }, + { + 'name': 'mail', + 'error': 'mail.a.b.c', + 'status': 'UP' + } + ]; + expect(service.transformHealthData(data)).toEqual(expected); + }); + + it('should flatten health data with subsystems at level 1, main system has no additional information', () => { + const data = { + 'status': 'UP', + 'db': { + 'status': 'UP', + 'database': 'H2', + 'hello': '1' + }, + 'mail': { + 'status': 'UP', + 'error': 'mail.a.b.c' + }, + 'system': { + 'status': 'DOWN', + 'subsystem1': { + 'status': 'UP', + 'property1': 'system.subsystem1.property1' + }, + 'subsystem2': { + 'status': 'DOWN', + 'error': 'system.subsystem1.error', + 'property2': 'system.subsystem2.property2' + } + } + }; + const expected = [ + { + 'name': 'db', + 'error': undefined, + 'status': 'UP', + 'details': { + 'database': 'H2', + 'hello': '1' + } + }, + { + 'name': 'mail', + 'error': 'mail.a.b.c', + 'status': 'UP' + }, + { + 'name': 'system.subsystem1', + 'error': undefined, + 'status': 'UP', + 'details': { + 'property1': 'system.subsystem1.property1' + } + }, + { + 'name': 'system.subsystem2', + 'error': 'system.subsystem1.error', + 'status': 'DOWN', + 'details': { + 'property2': 'system.subsystem2.property2' + } + } + ]; + expect(service.transformHealthData(data)).toEqual(expected); + }); + + it('should flatten health data with subsystems at level 1, main system has additional information', () => { + const data = { + 'status': 'UP', + 'db': { + 'status': 'UP', + 'database': 'H2', + 'hello': '1' + }, + 'mail': { + 'status': 'UP', + 'error': 'mail.a.b.c' + }, + 'system': { + 'status': 'DOWN', + 'property1': 'system.property1', + 'subsystem1': { + 'status': 'UP', + 'property1': 'system.subsystem1.property1' + }, + 'subsystem2': { + 'status': 'DOWN', + 'error': 'system.subsystem1.error', + 'property2': 'system.subsystem2.property2' + } + } + }; + const expected = [ + { + 'name': 'db', + 'error': undefined, + 'status': 'UP', + 'details': { + 'database': 'H2', + 'hello': '1' + } + }, + { + 'name': 'mail', + 'error': 'mail.a.b.c', + 'status': 'UP' + }, + { + 'name': 'system', + 'error': undefined, + 'status': 'DOWN', + 'details': { + 'property1': 'system.property1' + } + }, + { + 'name': 'system.subsystem1', + 'error': undefined, + 'status': 'UP', + 'details': { + 'property1': 'system.subsystem1.property1' + } + }, + { + 'name': 'system.subsystem2', + 'error': 'system.subsystem1.error', + 'status': 'DOWN', + 'details': { + 'property2': 'system.subsystem2.property2' + } + } + ]; + expect(service.transformHealthData(data)).toEqual(expected); + }); + + it('should flatten health data with subsystems at level 1, main system has additional error', () => { + const data = { + 'status': 'UP', + 'db': { + 'status': 'UP', + 'database': 'H2', + 'hello': '1' + }, + 'mail': { + 'status': 'UP', + 'error': 'mail.a.b.c' + }, + 'system': { + 'status': 'DOWN', + 'error': 'show me', + 'subsystem1': { + 'status': 'UP', + 'property1': 'system.subsystem1.property1' + }, + 'subsystem2': { + 'status': 'DOWN', + 'error': 'system.subsystem1.error', + 'property2': 'system.subsystem2.property2' + } + } + }; + const expected = [ + { + 'name': 'db', + 'error': undefined, + 'status': 'UP', + 'details': { + 'database': 'H2', + 'hello': '1' + } + }, + { + 'name': 'mail', + 'error': 'mail.a.b.c', + 'status': 'UP' + }, + { + 'name': 'system', + 'error': 'show me', + 'status': 'DOWN' + }, + { + 'name': 'system.subsystem1', + 'error': undefined, + 'status': 'UP', + 'details': { + 'property1': 'system.subsystem1.property1' + } + }, + { + 'name': 'system.subsystem2', + 'error': 'system.subsystem1.error', + 'status': 'DOWN', + 'details': { + 'property2': 'system.subsystem2.property2' + } + } + ]; + expect(service.transformHealthData(data)).toEqual(expected); + }); + }); +}); diff --git a/jhipster/src/test/javascript/spec/app/entities/comment/comment-detail.component.spec.ts b/jhipster/src/test/javascript/spec/app/entities/comment/comment-detail.component.spec.ts new file mode 100644 index 000000000000..b7c6b77b8c02 --- /dev/null +++ b/jhipster/src/test/javascript/spec/app/entities/comment/comment-detail.component.spec.ts @@ -0,0 +1,79 @@ +import { ComponentFixture, TestBed, async, inject } from '@angular/core/testing'; +import { MockBackend } from '@angular/http/testing'; +import { Http, BaseRequestOptions } from '@angular/http'; +import { OnInit } from '@angular/core'; +import { DatePipe } from '@angular/common'; +import { ActivatedRoute } from '@angular/router'; +import { Observable } from 'rxjs/Rx'; +import { DateUtils, DataUtils } from 'ng-jhipster'; +import { JhiLanguageService } from 'ng-jhipster'; +import { MockLanguageService } from '../../../helpers/mock-language.service'; +import { MockActivatedRoute } from '../../../helpers/mock-route.service'; +import { CommentDetailComponent } from '../../../../../../main/webapp/app/entities/comment/comment-detail.component'; +import { CommentService } from '../../../../../../main/webapp/app/entities/comment/comment.service'; +import { Comment } from '../../../../../../main/webapp/app/entities/comment/comment.model'; + +describe('Component Tests', () => { + + describe('Comment Management Detail Component', () => { + let comp: CommentDetailComponent; + let fixture: ComponentFixture; + let service: CommentService; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [CommentDetailComponent], + providers: [ + MockBackend, + BaseRequestOptions, + DateUtils, + DataUtils, + DatePipe, + { + provide: ActivatedRoute, + useValue: new MockActivatedRoute({id: 123}) + }, + { + provide: Http, + useFactory: (backendInstance: MockBackend, defaultOptions: BaseRequestOptions) => { + return new Http(backendInstance, defaultOptions); + }, + deps: [MockBackend, BaseRequestOptions] + }, + { + provide: JhiLanguageService, + useClass: MockLanguageService + }, + CommentService + ] + }).overrideComponent(CommentDetailComponent, { + set: { + template: '' + } + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CommentDetailComponent); + comp = fixture.componentInstance; + service = fixture.debugElement.injector.get(CommentService); + }); + + + describe('OnInit', () => { + it('Should call load all on init', () => { + // GIVEN + + spyOn(service, 'find').and.returnValue(Observable.of(new Comment(10))); + + // WHEN + comp.ngOnInit(); + + // THEN + expect(service.find).toHaveBeenCalledWith(123); + expect(comp.comment).toEqual(jasmine.objectContaining({id:10})); + }); + }); + }); + +}); diff --git a/jhipster/src/test/javascript/spec/app/entities/post/post-detail.component.spec.ts b/jhipster/src/test/javascript/spec/app/entities/post/post-detail.component.spec.ts new file mode 100644 index 000000000000..3ccb9cf6ad68 --- /dev/null +++ b/jhipster/src/test/javascript/spec/app/entities/post/post-detail.component.spec.ts @@ -0,0 +1,79 @@ +import { ComponentFixture, TestBed, async, inject } from '@angular/core/testing'; +import { MockBackend } from '@angular/http/testing'; +import { Http, BaseRequestOptions } from '@angular/http'; +import { OnInit } from '@angular/core'; +import { DatePipe } from '@angular/common'; +import { ActivatedRoute } from '@angular/router'; +import { Observable } from 'rxjs/Rx'; +import { DateUtils, DataUtils } from 'ng-jhipster'; +import { JhiLanguageService } from 'ng-jhipster'; +import { MockLanguageService } from '../../../helpers/mock-language.service'; +import { MockActivatedRoute } from '../../../helpers/mock-route.service'; +import { PostDetailComponent } from '../../../../../../main/webapp/app/entities/post/post-detail.component'; +import { PostService } from '../../../../../../main/webapp/app/entities/post/post.service'; +import { Post } from '../../../../../../main/webapp/app/entities/post/post.model'; + +describe('Component Tests', () => { + + describe('Post Management Detail Component', () => { + let comp: PostDetailComponent; + let fixture: ComponentFixture; + let service: PostService; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [PostDetailComponent], + providers: [ + MockBackend, + BaseRequestOptions, + DateUtils, + DataUtils, + DatePipe, + { + provide: ActivatedRoute, + useValue: new MockActivatedRoute({id: 123}) + }, + { + provide: Http, + useFactory: (backendInstance: MockBackend, defaultOptions: BaseRequestOptions) => { + return new Http(backendInstance, defaultOptions); + }, + deps: [MockBackend, BaseRequestOptions] + }, + { + provide: JhiLanguageService, + useClass: MockLanguageService + }, + PostService + ] + }).overrideComponent(PostDetailComponent, { + set: { + template: '' + } + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PostDetailComponent); + comp = fixture.componentInstance; + service = fixture.debugElement.injector.get(PostService); + }); + + + describe('OnInit', () => { + it('Should call load all on init', () => { + // GIVEN + + spyOn(service, 'find').and.returnValue(Observable.of(new Post(10))); + + // WHEN + comp.ngOnInit(); + + // THEN + expect(service.find).toHaveBeenCalledWith(123); + expect(comp.post).toEqual(jasmine.objectContaining({id:10})); + }); + }); + }); + +}); diff --git a/jhipster/src/test/javascript/spec/entry.ts b/jhipster/src/test/javascript/spec/entry.ts new file mode 100644 index 000000000000..64edbafb93c4 --- /dev/null +++ b/jhipster/src/test/javascript/spec/entry.ts @@ -0,0 +1,19 @@ +/// +import 'core-js'; +import 'zone.js/dist/zone'; +import 'zone.js/dist/long-stack-trace-zone'; +import 'zone.js/dist/async-test'; +import 'zone.js/dist/fake-async-test'; +import 'zone.js/dist/sync-test'; +import 'zone.js/dist/proxy'; +import 'zone.js/dist/jasmine-patch'; +import 'rxjs'; +import 'intl/locale-data/jsonp/en-US.js'; +import { TestBed } from '@angular/core/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; + +TestBed.initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); + +declare let require: any; +const testsContext: any = require.context('./', true, /\.spec/); +testsContext.keys().forEach(testsContext); diff --git a/jhipster/src/test/javascript/spec/helpers/mock-account.service.ts b/jhipster/src/test/javascript/spec/helpers/mock-account.service.ts new file mode 100644 index 000000000000..e21c10a37004 --- /dev/null +++ b/jhipster/src/test/javascript/spec/helpers/mock-account.service.ts @@ -0,0 +1,26 @@ +import { SpyObject } from './spyobject'; +import { AccountService } from '../../../../main/webapp/app/shared/auth/account.service'; +import Spy = jasmine.Spy; + +export class MockAccountService extends SpyObject { + + getSpy: Spy; + saveSpy: Spy; + fakeResponse: any; + + constructor() { + super(AccountService); + + this.fakeResponse = null; + this.getSpy = this.spy('get').andReturn(this); + this.saveSpy = this.spy('save').andReturn(this); + } + + subscribe(callback: any) { + callback(this.fakeResponse); + } + + setResponse(json: any): void { + this.fakeResponse = json; + } +} diff --git a/jhipster/src/test/javascript/spec/helpers/mock-language.service.ts b/jhipster/src/test/javascript/spec/helpers/mock-language.service.ts new file mode 100644 index 000000000000..0c7dec92f18e --- /dev/null +++ b/jhipster/src/test/javascript/spec/helpers/mock-language.service.ts @@ -0,0 +1,26 @@ +import { SpyObject } from './spyobject'; +import { JhiLanguageService } from 'ng-jhipster'; +import Spy = jasmine.Spy; + +export class MockLanguageService extends SpyObject { + + getCurrentSpy: Spy; + fakeResponse: any; + + constructor() { + super(JhiLanguageService); + + this.fakeResponse = 'en'; + this.getCurrentSpy = this.spy('getCurrent').andReturn(Promise.resolve(this.fakeResponse)); + } + + init() {} + + changeLanguage(languageKey: string) {} + + setLocations(locations: string[]) {} + + addLocation(location: string) {} + + reload() {} +} diff --git a/jhipster/src/test/javascript/spec/helpers/mock-principal.service.ts b/jhipster/src/test/javascript/spec/helpers/mock-principal.service.ts new file mode 100644 index 000000000000..89b932b83c1b --- /dev/null +++ b/jhipster/src/test/javascript/spec/helpers/mock-principal.service.ts @@ -0,0 +1,20 @@ +import { SpyObject } from './spyobject'; +import { Principal } from '../../../../main/webapp/app/shared/auth/principal.service'; +import Spy = jasmine.Spy; + +export class MockPrincipal extends SpyObject { + + identitySpy: Spy; + fakeResponse: any; + + constructor() { + super(Principal); + + this.fakeResponse = {}; + this.identitySpy = this.spy('identity').andReturn(Promise.resolve(this.fakeResponse)); + } + + setResponse(json: any): void { + this.fakeResponse = json; + } +} diff --git a/jhipster/src/test/javascript/spec/helpers/mock-route.service.ts b/jhipster/src/test/javascript/spec/helpers/mock-route.service.ts new file mode 100644 index 000000000000..3ddb29172108 --- /dev/null +++ b/jhipster/src/test/javascript/spec/helpers/mock-route.service.ts @@ -0,0 +1,15 @@ +import { ActivatedRoute, Params } from '@angular/router'; +import { Observable } from 'rxjs'; + +export class MockActivatedRoute extends ActivatedRoute { + + constructor(parameters?: any) { + super(); + this.queryParams = Observable.of(parameters); + this.params = Observable.of(parameters); + } +} + +export class MockRouter { + navigate = jasmine.createSpy('navigate'); +} diff --git a/jhipster/src/test/javascript/spec/helpers/spyobject.ts b/jhipster/src/test/javascript/spec/helpers/spyobject.ts new file mode 100644 index 000000000000..4db41fb8df3e --- /dev/null +++ b/jhipster/src/test/javascript/spec/helpers/spyobject.ts @@ -0,0 +1,69 @@ +export interface GuinessCompatibleSpy extends jasmine.Spy { + /** By chaining the spy with and.returnValue, all calls to the function will return a specific + * value. */ + andReturn(val: any): void; + /** By chaining the spy with and.callFake, all calls to the spy will delegate to the supplied + * function. */ + andCallFake(fn: Function): GuinessCompatibleSpy; + /** removes all recorded calls */ + reset(); +} + +export class SpyObject { + static stub(object = null, config = null, overrides = null) { + if (!(object instanceof SpyObject)) { + overrides = config; + config = object; + object = new SpyObject(); + } + + let m = {}; + Object.keys(config).forEach((key) => m[key] = config[key]); + Object.keys(overrides).forEach((key) => m[key] = overrides[key]); + Object.keys(m).forEach((key) => { + object.spy(key).andReturn(m[key]); + }); + return object; + } + + constructor(type = null) { + if (type) { + Object.keys(type.prototype).forEach((prop) => { + let m = null; + try { + m = type.prototype[prop]; + } catch (e) { + // As we are creating spys for abstract classes, + // these classes might have getters that throw when they are accessed. + // As we are only auto creating spys for methods, this + // should not matter. + } + if (typeof m === 'function') { + this.spy(prop); + } + }); + } + } + + spy(name) { + if (!this[name]) { + this[name] = this._createGuinnessCompatibleSpy(name); + } + return this[name]; + } + + prop(name, value) { + this[name] = value; + } + + /** @internal */ + _createGuinnessCompatibleSpy(name): GuinessCompatibleSpy { + let newSpy: GuinessCompatibleSpy = < any > jasmine.createSpy(name); + newSpy.andCallFake = < any > newSpy.and.callFake; + newSpy.andReturn = < any > newSpy.and.returnValue; + newSpy.reset = < any > newSpy.calls.reset; + // revisit return null here (previously needed for rtts_assert). + newSpy.and.returnValue(null); + return newSpy; + } +} diff --git a/jhipster/src/test/javascript/spec/test.module.ts b/jhipster/src/test/javascript/spec/test.module.ts new file mode 100644 index 000000000000..65ce439cb01e --- /dev/null +++ b/jhipster/src/test/javascript/spec/test.module.ts @@ -0,0 +1,24 @@ +import { NgModule } from '@angular/core'; +import { MockBackend } from '@angular/http/testing'; +import { Http, BaseRequestOptions } from '@angular/http'; +import { JhiLanguageService } from 'ng-jhipster'; +import { MockLanguageService } from './helpers/mock-language.service'; + +@NgModule({ + providers: [ + MockBackend, + BaseRequestOptions, + { + provide: JhiLanguageService, + useClass: MockLanguageService + }, + { + provide: Http, + useFactory: (backendInstance: MockBackend, defaultOptions: BaseRequestOptions) => { + return new Http(backendInstance, defaultOptions); + }, + deps: [MockBackend, BaseRequestOptions] + } + ] +}) +export class BaeldungTestModule {} diff --git a/jhipster/src/test/resources/config/application.yml b/jhipster/src/test/resources/config/application.yml new file mode 100644 index 000000000000..a7939c838cf2 --- /dev/null +++ b/jhipster/src/test/resources/config/application.yml @@ -0,0 +1,96 @@ +# =================================================================== +# Spring Boot configuration. +# +# This configuration is used for unit/integration tests. +# +# More information on profiles: https://jhipster.github.io/profiles/ +# More information on configuration properties: https://jhipster.github.io/common-application-properties/ +# =================================================================== + +# =================================================================== +# Standard Spring Boot properties. +# Full reference is available at: +# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html +# =================================================================== + + +spring: + application: + name: baeldung + jackson: + serialization.write_dates_as_timestamps: false + cache: + type: none + datasource: + type: com.zaxxer.hikari.HikariDataSource + url: jdbc:h2:mem:baeldung;DB_CLOSE_DELAY=-1 + name: + username: + password: + jpa: + database-platform: io.github.jhipster.domain.util.FixedH2Dialect + database: H2 + open-in-view: false + show-sql: true + hibernate: + ddl-auto: none + naming: + physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy + implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy + properties: + hibernate.id.new_generator_mappings: true + hibernate.cache.use_second_level_cache: false + hibernate.cache.use_query_cache: false + hibernate.generate_statistics: true + hibernate.hbm2ddl.auto: validate + mail: + host: localhost + messages: + basename: i18n/messages + mvc: + favicon: + enabled: false + thymeleaf: + mode: XHTML + +liquibase: + contexts: test + +security: + basic: + enabled: false + +server: + port: 10344 + address: localhost + +# =================================================================== +# JHipster specific properties +# +# Full reference is available at: https://jhipster.github.io/common-application-properties/ +# =================================================================== + +jhipster: + async: + core-pool-size: 2 + max-pool-size: 50 + queue-capacity: 10000 + security: + authentication: + jwt: + secret: e1d4b69d3f953e3fa622121e882e6f459ca20ca4 + # Token is valid 24 hours + token-validity-in-seconds: 86400 + metrics: # DropWizard Metrics configuration, used by MetricsConfiguration + jmx.enabled: true + +# =================================================================== +# Application specific properties +# Add your own application properties here, see the ApplicationProperties class +# to have type-safe configuration, like in the JHipsterProperties above +# +# More documentation is available at: +# https://jhipster.github.io/common-application-properties/ +# =================================================================== + +application: diff --git a/jhipster/src/test/resources/logback-test.xml b/jhipster/src/test/resources/logback-test.xml new file mode 100644 index 000000000000..c0acd0040185 --- /dev/null +++ b/jhipster/src/test/resources/logback-test.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/jhipster/tsconfig.json b/jhipster/tsconfig.json new file mode 100644 index 000000000000..354ae048ad6f --- /dev/null +++ b/jhipster/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "commonjs", + "moduleResolution": "node", + "sourceMap": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "removeComments": false, + "noImplicitAny": false, + "suppressImplicitAnyIndexErrors": true, + "outDir": "target/www/app", + "lib": ["es6", "dom"], + "typeRoots": [ + "node_modules/@types" + ] + }, + "include": [ + "src/main/webapp/app", + "src/test/javascript" + ] +} \ No newline at end of file diff --git a/jhipster/tslint.json b/jhipster/tslint.json new file mode 100644 index 000000000000..ee6491cf6971 --- /dev/null +++ b/jhipster/tslint.json @@ -0,0 +1,107 @@ +{ + "rulesDirectory": [ + "node_modules/codelyzer" + ], + "rules": { + "class-name": true, + "comment-format": [ + true, + "check-space" + ], + "curly": true, + "eofline": true, + "forin": true, + "indent": [ + true, + "spaces" + ], + "label-position": true, + "max-line-length": [ + true, + 140 + ], + "member-access": false, + "member-ordering": [ + true, + "static-before-instance", + "variables-before-functions" + ], + "no-arg": true, + "no-bitwise": true, + "no-console": [ + true, + "debug", + "info", + "time", + "timeEnd", + "trace" + ], + "no-construct": true, + "no-debugger": true, + "no-duplicate-variable": true, + "no-empty": false, + "no-eval": true, + "no-inferrable-types": true, + "no-shadowed-variable": true, + "no-string-literal": false, + "no-switch-case-fall-through": true, + "no-trailing-whitespace": true, + "no-unused-expression": true, + "no-use-before-declare": true, + "no-var-keyword": true, + "object-literal-sort-keys": false, + "one-line": [ + true, + "check-open-brace", + "check-catch", + "check-else", + "check-whitespace" + ], + "quotemark": [ + true, + "single" + ], + "radix": true, + "semicolon": [ + "always" + ], + "triple-equals": [ + true, + "allow-null-check" + ], + "typedef-whitespace": [ + true, + { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + } + ], + "variable-name": false, + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" + ], + + "directive-selector": [true, "attribute", "jhi", "camelCase"], + "component-selector": [true, "element", "jhi", "kebab-case"], + "use-input-property-decorator": true, + "use-output-property-decorator": true, + "use-host-property-decorator": true, + "no-input-rename": true, + "no-output-rename": true, + "use-life-cycle-interface": true, + "use-pipe-transform-interface": false, + "component-class-suffix": true, + "directive-class-suffix": true, + "no-access-missing-member": true, + "templates-use-public": true, + "invoke-injectable": true + } +} diff --git a/jhipster/webpack/webpack.common.js b/jhipster/webpack/webpack.common.js new file mode 100644 index 000000000000..4916bb2db599 --- /dev/null +++ b/jhipster/webpack/webpack.common.js @@ -0,0 +1,117 @@ +const webpack = require('webpack'); +const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const StringReplacePlugin = require('string-replace-webpack-plugin'); +const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin'); +const path = require('path'); + +module.exports = function (options) { + const DATAS = { + VERSION: JSON.stringify(require("../package.json").version), + DEBUG_INFO_ENABLED: options.env === 'dev' + }; + return { + entry: { + 'polyfills': './src/main/webapp/app/polyfills', + 'global': './src/main/webapp/content/scss/global.scss', + 'main': './src/main/webapp/app/app.main' + }, + resolve: { + extensions: ['.ts', '.js'], + modules: ['node_modules'] + }, + module: { + rules: [ + { test: /bootstrap\/dist\/js\/umd\//, loader: 'imports-loader?jQuery=jquery' }, + { + test: /\.ts$/, + loaders: [ + 'angular2-template-loader', + 'awesome-typescript-loader' + ], + exclude: ['node_modules/generator-jhipster'] + }, + { + test: /\.html$/, + loader: 'raw-loader', + exclude: ['./src/main/webapp/index.html'] + }, + { + test: /\.scss$/, + loaders: ['to-string-loader', 'css-loader', 'sass-loader'], + exclude: /(vendor\.scss|global\.scss)/ + }, + { + test: /(vendor\.scss|global\.scss)/, + loaders: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'] + }, + { + test: /\.css$/, + loaders: ['to-string-loader', 'css-loader'], + exclude: /(vendor\.css|global\.css)/ + }, + { + test: /(vendor\.css|global\.css)/, + loaders: ['style-loader', 'css-loader'] + }, + { + test: /\.(jpe?g|png|gif|svg|woff|woff2|ttf|eot)$/i, + loaders: [ + 'file-loader?hash=sha512&digest=hex&name=[hash].[ext]', { + loader: 'image-webpack-loader', + query: { + gifsicle: { + interlaced: false + }, + optipng: { + optimizationLevel: 7 + } + } + } + ] + }, + { + test: /app.constants.ts$/, + loader: StringReplacePlugin.replace({ + replacements: [{ + pattern: /\/\* @toreplace (\w*?) \*\//ig, + replacement: function (match, p1, offset, string) { + return `_${p1} = ${DATAS[p1]};`; + } + }] + }) + } + ] + }, + plugins: [ + new CommonsChunkPlugin({ + names: ['manifest', 'polyfills'].reverse() + }), + new webpack.DllReferencePlugin({ + context: './', + manifest: require(path.resolve('./target/www/vendor.json')), + }), + new CopyWebpackPlugin([ + { from: './node_modules/swagger-ui/dist', to: 'swagger-ui/dist' }, + { from: './src/main/webapp/swagger-ui/', to: 'swagger-ui' }, + { from: './src/main/webapp/favicon.ico', to: 'favicon.ico' }, + { from: './src/main/webapp/robots.txt', to: 'robots.txt' }, + { from: './src/main/webapp/i18n', to: 'i18n' } + ]), + new webpack.ProvidePlugin({ + $: "jquery", + jQuery: "jquery" + }), + new HtmlWebpackPlugin({ + template: './src/main/webapp/index.html', + chunksSortMode: 'dependency', + inject: 'body' + }), + new AddAssetHtmlPlugin([ + { filepath: path.resolve('./target/www/vendor.dll.js'), includeSourcemap: false } + ]), + new StringReplacePlugin() + ] + }; +}; diff --git a/jhipster/webpack/webpack.dev.js b/jhipster/webpack/webpack.dev.js new file mode 100644 index 000000000000..f612a44b092a --- /dev/null +++ b/jhipster/webpack/webpack.dev.js @@ -0,0 +1,65 @@ +const webpack = require('webpack'); +const path = require('path'); +const commonConfig = require('./webpack.common.js'); +const writeFilePlugin = require('write-file-webpack-plugin'); +const webpackMerge = require('webpack-merge'); +const BrowserSyncPlugin = require('browser-sync-webpack-plugin'); +const ExtractTextPlugin = require("extract-text-webpack-plugin"); +const ENV = 'dev'; +const execSync = require('child_process').execSync; +const fs = require('fs'); +const ddlPath = './target/www/vendor.json'; + +if (!fs.existsSync(ddlPath)) { + execSync('webpack --config webpack/webpack.vendor.js'); +} + +module.exports = webpackMerge(commonConfig({ env: ENV }), { + devtool: 'inline-source-map', + devServer: { + contentBase: './target/www', + proxy: [{ + context: [ + '/api', + '/management', + '/swagger-resources', + '/v2/api-docs', + '/h2-console' + ], + target: 'http://127.0.0.1:8080', + secure: false + }] + }, + output: { + path: path.resolve('target/www'), + filename: '[name].bundle.js', + chunkFilename: '[id].chunk.js' + }, + module: { + rules: [{ + test: /\.ts$/, + loaders: [ + 'tslint-loader' + ], + exclude: ['node_modules', new RegExp('reflect-metadata\\' + path.sep + 'Reflect\\.ts')] + }] + }, + plugins: [ + new BrowserSyncPlugin({ + host: 'localhost', + port: 9000, + proxy: { + target: 'http://localhost:9060' + } + }, { + reload: false + }), + new ExtractTextPlugin('styles.css'), + new webpack.NoEmitOnErrorsPlugin(), + new webpack.NamedModulesPlugin(), + new writeFilePlugin(), + new webpack.WatchIgnorePlugin([ + path.resolve('./src/test'), + ]) + ] +}); diff --git a/jhipster/webpack/webpack.prod.js b/jhipster/webpack/webpack.prod.js new file mode 100644 index 000000000000..28f0f2152bf2 --- /dev/null +++ b/jhipster/webpack/webpack.prod.js @@ -0,0 +1,22 @@ +const commonConfig = require('./webpack.common.js'); +const webpackMerge = require('webpack-merge'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); +const ExtractTextPlugin = require("extract-text-webpack-plugin"); +const Visualizer = require('webpack-visualizer-plugin'); +const ENV = 'prod'; + +module.exports = webpackMerge(commonConfig({ env: ENV }), { + devtool: 'source-map', + output: { + path: './target/www', + filename: '[hash].[name].bundle.js', + chunkFilename: '[hash].[id].chunk.js' + }, + plugins: [ + new ExtractTextPlugin('[hash].styles.css'), + new Visualizer({ + // Webpack statistics in target folder + filename: '../stats.html' + }) + ] +}); diff --git a/jhipster/webpack/webpack.vendor.js b/jhipster/webpack/webpack.vendor.js new file mode 100644 index 000000000000..449024d102fe --- /dev/null +++ b/jhipster/webpack/webpack.vendor.js @@ -0,0 +1,63 @@ +var webpack = require('webpack'); +module.exports = { + entry: { + 'vendor': [ + './src/main/webapp/app/vendor', + '@angular/common', + '@angular/compiler', + '@angular/core', + '@angular/forms', + '@angular/http', + '@angular/platform-browser', + '@angular/platform-browser-dynamic', + '@angular/router', + '@ng-bootstrap/ng-bootstrap', + 'angular2-cookie', + 'angular2-infinite-scroll', + 'jquery', + 'ng-jhipster', + 'ng2-webstorage', + 'rxjs' + ] + }, + resolve: { + extensions: ['.ts', '.js'], + modules: ['node_modules'] + }, + module: { + exprContextCritical: false, + rules: [ + { + test: /(vendor\.scss|global\.scss)/, + loaders: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'] + }, + { + test: /\.(jpe?g|png|gif|svg|woff|woff2|ttf|eot)$/i, + loaders: [ + 'file-loader?hash=sha512&digest=hex&name=[hash].[ext]', { + loader: 'image-webpack-loader', + query: { + gifsicle: { + interlaced: false + }, + optipng: { + optimizationLevel: 7 + } + } + } + ] + } + ] + }, + output: { + filename: '[name].dll.js', + path: './target/www', + library: '[name]' + }, + plugins: [ + new webpack.DllPlugin({ + name: '[name]', + path: './target/www/[name].json' + }) + ] +}; diff --git a/jooq/README.md b/jooq/README.md new file mode 100644 index 000000000000..2f09cab46bf6 --- /dev/null +++ b/jooq/README.md @@ -0,0 +1,3 @@ +### Relevant articles + +- [Introduction to jOOL](http://www.baeldung.com/jool) diff --git a/jooq/src/test/java/com/baeldung/JOOLTest.java b/jooq/src/test/java/com/baeldung/JOOLTest.java index 13bf1a3ec30d..a0d5f4037f4a 100644 --- a/jooq/src/test/java/com/baeldung/JOOLTest.java +++ b/jooq/src/test/java/com/baeldung/JOOLTest.java @@ -1,12 +1,9 @@ package com.baeldung; - -import junit.framework.Assert; import org.jooq.lambda.Seq; import org.jooq.lambda.Unchecked; import org.jooq.lambda.function.Function1; import org.jooq.lambda.function.Function2; -import org.jooq.lambda.tuple.Tuple; import org.jooq.lambda.tuple.Tuple2; import org.jooq.lambda.tuple.Tuple3; import org.jooq.lambda.tuple.Tuple4; diff --git a/jpa-storedprocedure/pom.xml b/jpa-storedprocedure/pom.xml index 797303dc295d..1672afd21705 100644 --- a/jpa-storedprocedure/pom.xml +++ b/jpa-storedprocedure/pom.xml @@ -30,6 +30,7 @@ maven-assembly-plugin + ${project.basedir} jar-with-dependencies diff --git a/jsf/pom.xml b/jsf/pom.xml index 271a8695761d..a206870f893a 100644 --- a/jsf/pom.xml +++ b/jsf/pom.xml @@ -65,6 +65,12 @@ org.springframework spring-web ${org.springframework.version} + + + commons-logging + commons-logging + + javax.servlet @@ -92,11 +98,6 @@ ${org.slf4j.version} - - org.slf4j - log4j-over-slf4j - ${org.slf4j.version} - diff --git a/jsf/src/main/resources/logback.xml b/jsf/src/main/resources/logback.xml index e9ae1894a660..ec0dc2469ae0 100644 --- a/jsf/src/main/resources/logback.xml +++ b/jsf/src/main/resources/logback.xml @@ -1,5 +1,5 @@ + - web - %date [%thread] %-5level %logger{36} - %message%n @@ -7,8 +7,13 @@ + + + + + + - \ No newline at end of file diff --git a/json/pom.xml b/json/pom.xml index 2b2f80e98782..f22626ce7061 100644 --- a/json/pom.xml +++ b/json/pom.xml @@ -11,6 +11,12 @@ org.everit.json org.everit.json.schema ${everit.json.schema.version} + + + commons-logging + commons-logging + + diff --git a/jsoup/src/test/java/com/baeldung/jsoup/JsoupParserTest.java b/jsoup/src/test/java/com/baeldung/jsoup/JsoupParserIntegrationTest.java similarity index 98% rename from jsoup/src/test/java/com/baeldung/jsoup/JsoupParserTest.java rename to jsoup/src/test/java/com/baeldung/jsoup/JsoupParserIntegrationTest.java index ba6d7358bc6d..dadd57b0ed78 100644 --- a/jsoup/src/test/java/com/baeldung/jsoup/JsoupParserTest.java +++ b/jsoup/src/test/java/com/baeldung/jsoup/JsoupParserIntegrationTest.java @@ -14,9 +14,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -public class JsoupParserTest { +public class JsoupParserIntegrationTest { - Document doc; + private Document doc; @Before public void setUp() throws IOException { diff --git a/jws/.gitignore b/jws/.gitignore new file mode 100644 index 000000000000..6346853947a5 --- /dev/null +++ b/jws/.gitignore @@ -0,0 +1,7 @@ +/target/ +.classpath +.project +.settings +/.settings/ +/settings/ +.tern-project diff --git a/jws/README.md b/jws/README.md new file mode 100644 index 000000000000..428d8b650d8a --- /dev/null +++ b/jws/README.md @@ -0,0 +1,3 @@ +### Relevant articles + +- [A Guide to the Java Web Start](http://www.baeldung.com/java-web-start) diff --git a/jws/java-core-samples-lib/jardiff.jar b/jws/java-core-samples-lib/jardiff.jar new file mode 100644 index 000000000000..08b5bb61ab00 Binary files /dev/null and b/jws/java-core-samples-lib/jardiff.jar differ diff --git a/jws/java-core-samples-lib/jnlp-servlet.jar b/jws/java-core-samples-lib/jnlp-servlet.jar new file mode 100644 index 000000000000..c27167c64700 Binary files /dev/null and b/jws/java-core-samples-lib/jnlp-servlet.jar differ diff --git a/jws/pom.xml b/jws/pom.xml new file mode 100644 index 000000000000..dea98fb41a82 --- /dev/null +++ b/jws/pom.xml @@ -0,0 +1,86 @@ + + + 4.0.0 + com.example + jws + war + 0.0.1-SNAPSHOT + + jws-example + + + 3.0.2 + 3.0.0 + 7.0 + 7.0 + + + + + javax.jnlp + jnlp-servlet + ${jnlp-servlet.version} + system + ${project.basedir}/java-core-samples-lib/jnlp-servlet.jar + + + javax.jnlp + jardiff + ${jardiff.version} + system + ${project.basedir}/java-core-samples-lib/jardiff.jar + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven-jar-plugin.version} + + + compile + + jar + + + + + com.example.Hello + + + ${project.basedir}/target/jws + + + + + + + org.apache.maven.plugins + maven-war-plugin + ${maven-war-plugin.version} + + + package + + + + + + ${project.basedir}/java-core-samples-lib/ + + **/*.jar + + WEB-INF/lib + + + + + + ${project.artifactId} + + + diff --git a/jws/src/main/java/com/example/Hello.java b/jws/src/main/java/com/example/Hello.java new file mode 100644 index 000000000000..3479277ace02 --- /dev/null +++ b/jws/src/main/java/com/example/Hello.java @@ -0,0 +1,15 @@ +package com.example; + +import javax.swing.*; + +public class Hello { + public static void main(String[] args) { + JFrame f = new JFrame("main"); + f.setSize(200, 100); + f.setLocationRelativeTo(null); + f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + JLabel label = new JLabel("Hello, world!"); + f.add(label); + f.setVisible(true); + } +} diff --git a/jws/src/main/webapp/WEB-INF/web.xml b/jws/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000000..18f29ddd128a --- /dev/null +++ b/jws/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,24 @@ + + + Java Web Start + JNLP Example for Java Web Start Article + + + JnlpDownloadServlet + jnlp.sample.servlet.JnlpDownloadServlet + + + JnlpDownloadServlet + *.jar + + + JnlpDownloadServlet + *.jnlp + + + + index.html + + diff --git a/jws/src/main/webapp/hello.jnlp b/jws/src/main/webapp/hello.jnlp new file mode 100644 index 000000000000..950c0d716fa0 --- /dev/null +++ b/jws/src/main/webapp/hello.jnlp @@ -0,0 +1,12 @@ + + + + Hello + Example + + + + + + + diff --git a/jws/src/main/webapp/index.html b/jws/src/main/webapp/index.html new file mode 100644 index 000000000000..212607b8504c --- /dev/null +++ b/jws/src/main/webapp/index.html @@ -0,0 +1,10 @@ + + +Hello World JNLP + + +

+ Launch the example +

+ + diff --git a/kotlin/README.md b/kotlin/README.md index 6447a26f5c51..309aafa4b6f1 100644 --- a/kotlin/README.md +++ b/kotlin/README.md @@ -1,3 +1,5 @@ ## Relevant articles: - [Introduction to the Kotlin Language](http://www.baeldung.com/kotlin) +- [A guide to the “when{}” block in Kotlin](http://www.baeldung.com/kotlin-when) +- [Comprehensive Guide to Null Safety in Kotlin](http://www.baeldung.com/kotlin-null-safety) diff --git a/kotlin/pom.xml b/kotlin/pom.xml index f928cc40375c..07fb6863d40f 100644 --- a/kotlin/pom.xml +++ b/kotlin/pom.xml @@ -90,10 +90,9 @@ 4.12 - 1.0.6 - 1.0.6 - 1.0.6 - 1.0.6 + 1.1.1 + 1.1.1 + 1.1.1 \ No newline at end of file diff --git a/kotlin/src/main/kotlin/com/baeldung/kotlin/WhenBlockTypes.kt b/kotlin/src/main/kotlin/com/baeldung/kotlin/WhenBlockTypes.kt new file mode 100644 index 000000000000..6180da10d920 --- /dev/null +++ b/kotlin/src/main/kotlin/com/baeldung/kotlin/WhenBlockTypes.kt @@ -0,0 +1,28 @@ +package com.baeldung.kotlin + +enum class UnixFileType { + D, HYPHEN_MINUS, L +} + +sealed class UnixFile { + + abstract fun getFileType(): UnixFileType + + class RegularFile(val content: String) : UnixFile() { + override fun getFileType(): UnixFileType { + return UnixFileType.HYPHEN_MINUS + } + } + + class Directory(val children: List) : UnixFile() { + override fun getFileType(): UnixFileType { + return UnixFileType.D + } + } + + class SymbolicLink(val originalFile: UnixFile) : UnixFile() { + override fun getFileType(): UnixFileType { + return UnixFileType.L + } + } +} \ No newline at end of file diff --git a/kotlin/src/test/kotlin/com/baeldung/kotlin/GenericsTest.kt b/kotlin/src/test/kotlin/com/baeldung/kotlin/GenericsTest.kt new file mode 100644 index 000000000000..b189d0f483cd --- /dev/null +++ b/kotlin/src/test/kotlin/com/baeldung/kotlin/GenericsTest.kt @@ -0,0 +1,143 @@ +package com.baeldung.kotlin + +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class GenericsTest { + + @Test + fun givenParametrizeClass_whenInitializeItWithSpecificType_thenShouldBeParameterized() { + //given + val parameterizedClass = ParameterizedClass("string-value") + + //when + val res = parameterizedClass.getValue() + + //then + assertTrue(res is String) + } + + @Test + fun givenParametrizeClass_whenInitializeIt_thenShouldBeParameterizedByInferredType() { + //given + val parameterizedClass = ParameterizedClass("string-value") + + //when + val res = parameterizedClass.getValue() + + //then + assertTrue(res is String) + } + + @Test + fun givenParameterizedProducerByOutKeyword_whenGetValue_thenCanAssignItToSuperType() { + //given + val parameterizedProducer = ParameterizedProducer("string") + + //when + val ref: ParameterizedProducer = parameterizedProducer + + //then + assertTrue(ref is ParameterizedProducer) + } + + @Test + fun givenParameterizedConsumerByInKeyword_whenGetValue_thenCanAssignItToSubType() { + //given + val parameterizedConsumer = ParameterizedConsumer() + + //when + val ref: ParameterizedConsumer = parameterizedConsumer + + //then + assertTrue(ref is ParameterizedConsumer) + } + + @Test + fun givenTypeProjections_whenOperateOnTwoList_thenCanAcceptListOfSubtypes() { + //given + val ints: Array = arrayOf(1, 2, 3) + val any: Array = arrayOfNulls(3) + + //when + copy(ints, any) + + //then + assertEquals(any[0], 1) + assertEquals(any[1], 2) + assertEquals(any[2], 3) + + } + + fun copy(from: Array, to: Array) { + assert(from.size == to.size) + for (i in from.indices) + to[i] = from[i] + } + + @Test + fun givenTypeProjection_whenHaveArrayOfIn_thenShouldAddElementsOfSubtypesToIt() { + //given + val objects: Array = arrayOfNulls(1) + + //when + fill(objects, 1) + + //then + assertEquals(objects[0], 1) + } + + fun fill(dest: Array, value: Int) { + dest[0] = value + } + + @Test + fun givenStartProjection_whenPassAnyType_thenCompile() { + //given + val array = arrayOf(1,2,3) + + //then + printArray(array) + + } + + fun printArray(array: Array<*>) { + array.forEach { println(it) } + } + + @Test + fun givenFunctionWithDefinedGenericConstraints_whenCallWithProperType_thenCompile(){ + //given + val listOfInts = listOf(5,2,3,4,1) + + //when + val sorted = sort(listOfInts) + + //then + assertEquals(sorted, listOf(1,2,3,4,5)) + } + + fun > sort(list: List): List{ + return list.sorted() + } + + class ParameterizedClass(private val value: A) { + + fun getValue(): A { + return value + } + } + + class ParameterizedProducer(private val value: T) { + fun get(): T { + return value + } + } + + class ParameterizedConsumer { + fun toString(value: T): String { + return value.toString() + } + } +} diff --git a/kotlin/src/test/kotlin/com/baeldung/kotlin/NullSafetyTest.kt b/kotlin/src/test/kotlin/com/baeldung/kotlin/NullSafetyTest.kt new file mode 100644 index 000000000000..0ecc74b6fb4e --- /dev/null +++ b/kotlin/src/test/kotlin/com/baeldung/kotlin/NullSafetyTest.kt @@ -0,0 +1,160 @@ +package com.baeldung.kotlin + +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertNull +import kotlin.test.assertTrue + + +class NullSafetyTest { + @Test + fun givenNonNullableField_whenAssignValueToIt_thenNotNeedToCheckAgainstNull() { + //given + var a: String = "value" + //a = null compilation error + + //then + assertEquals(a.length, 5) + } + + @Test + fun givenNullableField_whenReadValue_thenNeedToCheckAgainstNull() { + //given + var b: String? = "value" + b = null + + //when + if (b != null) { + + } else { + assertNull(b) + } + } + + @Test + fun givenComplexObject_whenUseSafeCall_thenShouldChainCallsResultingWithValue() { + //given + val p: Person? = Person(Country("ENG")) + + //when + val res = p?.country?.code + + //then + assertEquals(res, "ENG") + } + + @Test + fun givenComplexObject_whenUseSafeCall_thenShouldChainCallsResultingWithNull() { + //given + val p: Person? = Person(Country(null)) + + //when + val res = p?.country?.code + + //then + assertNull(res) + } + + @Test + fun givenCollectionOfObjects_whenUseLetOperator_thenShouldApplyActionOnlyOnNonNullValue() { + //given + val firstName = "Tom" + val secondName = "Michael" + val names: List = listOf(firstName, null, secondName) + + //when + var res = listOf() + for (item in names) { + item?.let { res = res.plus(it); it } + ?.also{it -> println("non nullable value: $it")} + } + + //then + assertEquals(2, res.size) + assertTrue { res.contains(firstName) } + assertTrue { res.contains(secondName) } + } + + @Test + fun fivenCollectionOfObject_whenUseRunOperator_thenExecuteActionOnNonNullValue(){ + //given + val firstName = "Tom" + val secondName = "Michael" + val names: List = listOf(firstName, null, secondName) + + //when + var res = listOf() + for (item in names) { + item?.run{res = res.plus(this)} + } + + //then + assertEquals(2, res.size) + assertTrue { res.contains(firstName) } + assertTrue { res.contains(secondName) } + } + + @Test + fun givenNullableReference_whenUseElvisOperator_thenShouldReturnValueIfReferenceIsNotNull() { + //given + val value: String? = "name" + + //when + val res = value?.length ?: -1 + + //then + assertEquals(res, 4) + } + + @Test + fun givenNullableReference_whenUseElvisOperator_thenShouldReturnDefaultValueIfReferenceIsNull() { + //given + val value: String? = null + + //when + val res = value?.length ?: -1 + + //then + assertEquals(res, -1) + } + + @Test + fun givenNullableField_whenUsingDoubleExclamationMarkOperatorOnNull_thenThrowNPE() { + //given + var b: String? = "value" + b = null + + //when + assertFailsWith { + b!!.length + } + } + + @Test + fun givenNullableField_whenUsingDoubleExclamationMarkOperatorOnNotNull_thenReturnValue() { + //given + val b: String? = "value" + + //then + assertEquals(b!!.length, 5) + } + + @Test + fun givenNullableList_whenUseFilterNotNullMethod_thenRemoveALlNullValues() { + //given + val list: List = listOf("a", null, "b") + + //when + val res = list.filterNotNull() + + //then + assertEquals(res.size, 2) + assertTrue { res.contains("a") } + assertTrue { res.contains("b") } + } +} + +data class Person(val country: Country?) + +data class Country(val code: String?) \ No newline at end of file diff --git a/kotlin/src/test/kotlin/com/baeldung/kotlin/WhenBlockUnitTest.kt b/kotlin/src/test/kotlin/com/baeldung/kotlin/WhenBlockUnitTest.kt new file mode 100644 index 000000000000..aa1891fa4614 --- /dev/null +++ b/kotlin/src/test/kotlin/com/baeldung/kotlin/WhenBlockUnitTest.kt @@ -0,0 +1,136 @@ +package com.baeldung.kotlin + +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test + +class WhenBlockUnitTest { + + @Test + fun testWhenExpression() { + val directoryType = UnixFileType.D + + val objectType = when (directoryType) { + UnixFileType.D -> "d" + UnixFileType.HYPHEN_MINUS -> "-" + UnixFileType.L -> "l" + } + + assertEquals("d", objectType) + } + + @Test + fun testWhenExpressionWithDefaultCase() { + val fileType = UnixFileType.L + + val result = when (fileType) { + UnixFileType.L -> "linking to another file" + else -> "not a link" + } + + assertEquals("linking to another file", result) + } + + @Test(expected = IllegalArgumentException::class) + fun testWhenExpressionWithThrowException() { + val fileType = UnixFileType.L + + val result: Boolean = when (fileType) { + UnixFileType.HYPHEN_MINUS -> true + else -> throw IllegalArgumentException("Wrong type of file") + } + } + + @Test + fun testWhenStatement() { + val fileType = UnixFileType.HYPHEN_MINUS + + when (fileType) { + UnixFileType.HYPHEN_MINUS -> println("Regular file type") + UnixFileType.D -> println("Directory file type") + } + } + + @Test + fun testCaseCombination() { + val fileType = UnixFileType.D + + val frequentFileType: Boolean = when (fileType) { + UnixFileType.HYPHEN_MINUS, UnixFileType.D -> true + else -> false + } + + assertTrue(frequentFileType) + } + + @Test + fun testWhenWithoutArgument() { + val fileType = UnixFileType.L + + val objectType = when { + fileType === UnixFileType.L -> "l" + fileType === UnixFileType.HYPHEN_MINUS -> "-" + fileType === UnixFileType.D -> "d" + else -> "unknown file type" + } + + assertEquals("l", objectType) + } + + @Test + fun testDynamicCaseExpression() { + val unixFile = UnixFile.SymbolicLink(UnixFile.RegularFile("Content")) + + when { + unixFile.getFileType() == UnixFileType.D -> println("It's a directory!") + unixFile.getFileType() == UnixFileType.HYPHEN_MINUS -> println("It's a regular file!") + unixFile.getFileType() == UnixFileType.L -> println("It's a soft link!") + } + } + + @Test + fun testCollectionCaseExpressions() { + val regularFile = UnixFile.RegularFile("Test Content") + val symbolicLink = UnixFile.SymbolicLink(regularFile) + val directory = UnixFile.Directory(listOf(regularFile, symbolicLink)) + + val isRegularFileInDirectory = when (regularFile) { + in directory.children -> true + else -> false + } + + val isSymbolicLinkInDirectory = when { + symbolicLink in directory.children -> true + else -> false + } + + assertTrue(isRegularFileInDirectory) + assertTrue(isSymbolicLinkInDirectory) + } + + @Test + fun testRangeCaseExpressions() { + val fileType = UnixFileType.HYPHEN_MINUS + + val isCorrectType = when (fileType) { + in UnixFileType.D..UnixFileType.L -> true + else -> false + } + + assertTrue(isCorrectType) + } + + @Test + fun testWhenWithIsOperatorWithSmartCase() { + val unixFile: UnixFile = UnixFile.RegularFile("Test Content") + + val result = when (unixFile) { + is UnixFile.RegularFile -> unixFile.content + is UnixFile.Directory -> unixFile.children.map { it.getFileType() }.joinToString(", ") + is UnixFile.SymbolicLink -> unixFile.originalFile.getFileType() + } + + assertEquals("Test Content", result) + } + +} \ No newline at end of file diff --git a/lagom/.gitignore b/lagom/.gitignore new file mode 100644 index 000000000000..5e521241aa71 --- /dev/null +++ b/lagom/.gitignore @@ -0,0 +1,4 @@ +greeting-impl/logs/* +lagom-hello-world/greeting-impl/bin/classes/* +lagom-hello-world/logs/application.log +lagom-hello-world/weather-impl/logs/application.log diff --git a/lagom/README.md b/lagom/README.md new file mode 100644 index 000000000000..909fe0dff4f5 --- /dev/null +++ b/lagom/README.md @@ -0,0 +1,47 @@ +### Relevant articles + +- [Guide to Reactive Microservices Using Lagom Framework](http://www.baeldung.com/lagom-reactive-microservices) + + + + +Steps to setup from scratch + +1) Create sbt build file "build.sbt" +2) Create plugins file project/plugins.sbt +3) Create build properties file project/build.properties +4) Run sbt command from project root to generate project directories, generated projects at target/lagom-dynamic-projects + Service Locator project: lagom-internal-meta-project-service-locator + cassandra project: lagom-internal-meta-project-cassandra +5) Create folders in all projects to follow maven like directory structure layout for project source code: src/main/java and src/main/java/resources and test folders. +6) Weather microservice + a) WeatherService Interface with method: + weatherStatsForToday() + b) WeatherServiceImpl to return weatherstats from a random list of weather stats + +7) Greeting Microservice + GreetingService Interface: + handleGreetFrom(String user) + + a) Reply back to user with greeting message("Hello") along with weather stats fetched from weather microservice + b) Update the greeting message("Hello Again") for an existing user. + +8) GreetingEntity: PersistentEntity + a) handles ReceivedGreetingCommand + b) Stores ReceivedGreetingEvent events to cassandra + c) Processes ReceivedGreetingEvent to update GreetingState("Hello" to "Hello Again") if required. + +9) Register modules with lagom using application.conf files. +10) Run project: sbt lagom:runAll + + +Sample Run: +curl http://localhost:9000/api/greeting/Nikhil; +Hello Nikhil! Today's weather stats: Going to be very humid, Take Water + +curl http://localhost:9000/api/greeting/Nikhil; +Hello Again Nikhil! Today's weather stats: Going to Rain, Take Umbrella + + + + diff --git a/lagom/build.sbt b/lagom/build.sbt new file mode 100644 index 000000000000..06927e330179 --- /dev/null +++ b/lagom/build.sbt @@ -0,0 +1,41 @@ +organization in ThisBuild := "org.baeldung" + +// the Scala version that will be used for cross-compiled libraries +scalaVersion in ThisBuild := "2.11.8" + +lagomKafkaEnabled in ThisBuild := false + +lazy val greetingApi = project("greeting-api") + .settings( + version := "1.0-SNAPSHOT", + libraryDependencies ++= Seq( + lagomJavadslApi + ) + ) + +lazy val greetingImpl = project("greeting-impl") + .enablePlugins(LagomJava) + .settings( + version := "1.0-SNAPSHOT", + libraryDependencies ++= Seq( + lagomJavadslPersistenceCassandra + ) + ) + .dependsOn(greetingApi, weatherApi) + +lazy val weatherApi = project("weather-api") + .settings( + version := "1.0-SNAPSHOT", + libraryDependencies ++= Seq( + lagomJavadslApi + ) + ) + +lazy val weatherImpl = project("weather-impl") + .enablePlugins(LagomJava) + .settings( + version := "1.0-SNAPSHOT" + ) + .dependsOn(weatherApi) + +def project(id: String) = Project(id, base = file(id)) diff --git a/lagom/greeting-api/src/main/java/org/baeldung/lagom/helloworld/greeting/api/GreetingService.java b/lagom/greeting-api/src/main/java/org/baeldung/lagom/helloworld/greeting/api/GreetingService.java new file mode 100644 index 000000000000..93567f0185dc --- /dev/null +++ b/lagom/greeting-api/src/main/java/org/baeldung/lagom/helloworld/greeting/api/GreetingService.java @@ -0,0 +1,23 @@ +package org.baeldung.lagom.helloworld.greeting.api; + +import static com.lightbend.lagom.javadsl.api.Service.named; +import static com.lightbend.lagom.javadsl.api.Service.restCall; + +import com.lightbend.lagom.javadsl.api.Descriptor; +import com.lightbend.lagom.javadsl.api.Service; +import com.lightbend.lagom.javadsl.api.ServiceCall; +import com.lightbend.lagom.javadsl.api.transport.Method; + +import akka.NotUsed; + +public interface GreetingService extends Service { + + public ServiceCall handleGreetFrom(String user); + + @Override + default Descriptor descriptor() { + return named("greetingservice").withCalls( + restCall(Method.GET, "/api/greeting/:fromUser", this::handleGreetFrom) + ).withAutoAcl(true); + } +} \ No newline at end of file diff --git a/lagom/greeting-impl/bin/application.conf b/lagom/greeting-impl/bin/application.conf new file mode 100644 index 000000000000..1e78ce4a79ae --- /dev/null +++ b/lagom/greeting-impl/bin/application.conf @@ -0,0 +1 @@ +play.modules.enabled += org.baeldung.lagom.helloworld.greeting.impl.GreetingServiceModule \ No newline at end of file diff --git a/lagom/greeting-impl/bin/classes/application.conf b/lagom/greeting-impl/bin/classes/application.conf new file mode 100644 index 000000000000..1e78ce4a79ae --- /dev/null +++ b/lagom/greeting-impl/bin/classes/application.conf @@ -0,0 +1 @@ +play.modules.enabled += org.baeldung.lagom.helloworld.greeting.impl.GreetingServiceModule \ No newline at end of file diff --git a/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingCommand.java b/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingCommand.java new file mode 100644 index 000000000000..be9e713ec90f --- /dev/null +++ b/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingCommand.java @@ -0,0 +1,29 @@ +package org.baeldung.lagom.helloworld.greeting.impl; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.google.common.base.Preconditions; +import com.lightbend.lagom.javadsl.persistence.PersistentEntity; +import com.lightbend.lagom.serialization.CompressedJsonable; +import com.lightbend.lagom.serialization.Jsonable; + +public interface GreetingCommand extends Jsonable { + + @SuppressWarnings("serial") + @JsonDeserialize + public final class ReceivedGreetingCommand implements GreetingCommand, + CompressedJsonable, PersistentEntity.ReplyType { + private final String fromUser; + + @JsonCreator + public ReceivedGreetingCommand(String fromUser) { + this.fromUser = Preconditions.checkNotNull(fromUser, "fromUser"); + } + + public String getFromUser() { + return fromUser; + } + + } + +} diff --git a/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingEntity.java b/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingEntity.java new file mode 100644 index 000000000000..91fc74039d0d --- /dev/null +++ b/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingEntity.java @@ -0,0 +1,32 @@ +package org.baeldung.lagom.helloworld.greeting.impl; + +import java.util.Optional; + +import org.baeldung.lagom.helloworld.greeting.impl.GreetingCommand.ReceivedGreetingCommand; +import org.baeldung.lagom.helloworld.greeting.impl.GreetingEvent.ReceivedGreetingEvent; + +import com.lightbend.lagom.javadsl.persistence.PersistentEntity; + +public class GreetingEntity extends PersistentEntity { + + @Override + public Behavior initialBehavior(Optional snapshotState) { + BehaviorBuilder b = newBehaviorBuilder(new GreetingState("Hello ")); + + b.setCommandHandler(ReceivedGreetingCommand.class, + (cmd, ctx) -> { + String fromUser = cmd.getFromUser(); + String currentGreeting = state().getMessage(); + return ctx.thenPersist( + new ReceivedGreetingEvent(fromUser), + evt -> ctx.reply(currentGreeting + fromUser + "!")); + }); + + b.setEventHandler(ReceivedGreetingEvent.class, + // We simply update the current state + evt -> state().withMessage("Hello Again ")); + + return b.build(); + } +} diff --git a/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingEvent.java b/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingEvent.java new file mode 100644 index 000000000000..e454f6cd1b7e --- /dev/null +++ b/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingEvent.java @@ -0,0 +1,23 @@ +package org.baeldung.lagom.helloworld.greeting.impl; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.lightbend.lagom.serialization.Jsonable; + + +public interface GreetingEvent extends Jsonable { + + class ReceivedGreetingEvent implements GreetingEvent { + private final String fromUser; + + @JsonCreator + public ReceivedGreetingEvent(String fromUser) { + this.fromUser = fromUser; + } + + public String getFromUser() { + return fromUser; + } + + } + +} diff --git a/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingServiceImpl.java b/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingServiceImpl.java new file mode 100644 index 000000000000..c28687291e95 --- /dev/null +++ b/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingServiceImpl.java @@ -0,0 +1,48 @@ +package org.baeldung.lagom.helloworld.greeting.impl; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import org.baeldung.lagom.helloworld.greeting.api.GreetingService; +import org.baeldung.lagom.helloworld.greeting.impl.GreetingCommand.ReceivedGreetingCommand; +import org.baeldung.lagom.helloworld.weather.api.WeatherService; +import org.baeldung.lagom.helloworld.weather.api.WeatherStats; + +import com.google.inject.Inject; +import com.lightbend.lagom.javadsl.api.ServiceCall; +import com.lightbend.lagom.javadsl.persistence.PersistentEntityRef; +import com.lightbend.lagom.javadsl.persistence.PersistentEntityRegistry; + +import akka.NotUsed; + +public class GreetingServiceImpl implements GreetingService { + + private final PersistentEntityRegistry persistentEntityRegistry; + private final WeatherService weatherService; + + @Inject + public GreetingServiceImpl(PersistentEntityRegistry persistentEntityRegistry, WeatherService weatherService) { + this.persistentEntityRegistry = persistentEntityRegistry; + this.weatherService = weatherService; + persistentEntityRegistry.register(GreetingEntity.class); + } + + @Override + public ServiceCall handleGreetFrom(String user) { + return request -> { + // Look up the hello world entity for the given ID. + PersistentEntityRef ref = persistentEntityRegistry.refFor(GreetingEntity.class, user); + CompletableFuture greetingResponse = ref.ask(new ReceivedGreetingCommand(user)) + .toCompletableFuture(); + CompletableFuture todaysWeatherInfo = + (CompletableFuture) weatherService.weatherStatsForToday().invoke(); + try { + return CompletableFuture.completedFuture(greetingResponse.get() + + " Today's weather stats: " + todaysWeatherInfo.get().getMessage()); + } catch (InterruptedException | ExecutionException e) { + return CompletableFuture.completedFuture("Sorry Some Error at our end, working on it"); + } + }; + } + +} diff --git a/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingServiceModule.java b/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingServiceModule.java new file mode 100644 index 000000000000..4813e91a7cb3 --- /dev/null +++ b/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingServiceModule.java @@ -0,0 +1,18 @@ +package org.baeldung.lagom.helloworld.greeting.impl; + +import org.baeldung.lagom.helloworld.greeting.api.GreetingService; +import org.baeldung.lagom.helloworld.weather.api.WeatherService; + +import com.google.inject.AbstractModule; +import com.lightbend.lagom.javadsl.server.ServiceGuiceSupport; + +/** + * The module that binds the GreetingService so that it can be served. + */ +public class GreetingServiceModule extends AbstractModule implements ServiceGuiceSupport { + @Override + protected void configure() { + bindServices(serviceBinding(GreetingService.class, GreetingServiceImpl.class)); + bindClient(WeatherService.class); + } +} diff --git a/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingState.java b/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingState.java new file mode 100644 index 000000000000..125795601e1a --- /dev/null +++ b/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingState.java @@ -0,0 +1,24 @@ +package org.baeldung.lagom.helloworld.greeting.impl; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; + +@JsonDeserialize +public class GreetingState { + + private final String message; + + @JsonCreator + public GreetingState(String message) { + this.message = message; + } + + GreetingState withMessage(String message) { + return new GreetingState(message); + } + + public String getMessage() { + return message; + } + +} diff --git a/lagom/greeting-impl/src/main/resources/application.conf b/lagom/greeting-impl/src/main/resources/application.conf new file mode 100644 index 000000000000..1e78ce4a79ae --- /dev/null +++ b/lagom/greeting-impl/src/main/resources/application.conf @@ -0,0 +1 @@ +play.modules.enabled += org.baeldung.lagom.helloworld.greeting.impl.GreetingServiceModule \ No newline at end of file diff --git a/lagom/project/build.properties b/lagom/project/build.properties new file mode 100644 index 000000000000..43b8278c68cf --- /dev/null +++ b/lagom/project/build.properties @@ -0,0 +1 @@ +sbt.version=0.13.11 diff --git a/lagom/project/plugins.sbt b/lagom/project/plugins.sbt new file mode 100644 index 000000000000..8b8e36a743e7 --- /dev/null +++ b/lagom/project/plugins.sbt @@ -0,0 +1,2 @@ +addSbtPlugin("com.lightbend.lagom" % "lagom-sbt-plugin" % "1.3.1") +addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "3.0.0") diff --git a/lagom/weather-api/src/main/java/org/baeldung/lagom/helloworld/weather/api/WeatherService.java b/lagom/weather-api/src/main/java/org/baeldung/lagom/helloworld/weather/api/WeatherService.java new file mode 100644 index 000000000000..888109322b3f --- /dev/null +++ b/lagom/weather-api/src/main/java/org/baeldung/lagom/helloworld/weather/api/WeatherService.java @@ -0,0 +1,28 @@ +package org.baeldung.lagom.helloworld.weather.api; + +import static com.lightbend.lagom.javadsl.api.Service.named; +import static com.lightbend.lagom.javadsl.api.Service.*; + +import com.lightbend.lagom.javadsl.api.Descriptor; +import com.lightbend.lagom.javadsl.api.Service; +import com.lightbend.lagom.javadsl.api.ServiceCall; +import com.lightbend.lagom.javadsl.api.transport.Method; + +import akka.NotUsed; + +/** + * WeatherService Interface. + */ +public interface WeatherService extends Service { + + // Fetch Today's Weather Stats service call + public ServiceCall weatherStatsForToday(); + + @Override + default Descriptor descriptor() { + return named("weatherservice").withCalls( + restCall(Method.GET, "/api/weather", this::weatherStatsForToday) + ).withAutoAcl(true); + } + +} \ No newline at end of file diff --git a/lagom/weather-api/src/main/java/org/baeldung/lagom/helloworld/weather/api/WeatherStats.java b/lagom/weather-api/src/main/java/org/baeldung/lagom/helloworld/weather/api/WeatherStats.java new file mode 100644 index 000000000000..23530a297db7 --- /dev/null +++ b/lagom/weather-api/src/main/java/org/baeldung/lagom/helloworld/weather/api/WeatherStats.java @@ -0,0 +1,32 @@ +package org.baeldung.lagom.helloworld.weather.api; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Random; + +public enum WeatherStats { + + STATS_RAINY("Going to Rain, Take Umbrella"), STATS_HUMID("Going to be very humid, Take Water"); + + private final String message; + + private static final List VALUES = Collections.unmodifiableList(Arrays.asList(values())); + + private static final int SIZE = VALUES.size(); + + private static final Random RANDOM = new Random(); + + WeatherStats(String msg) { + this.message = msg; + } + + public static WeatherStats forToday() { + return VALUES.get(RANDOM.nextInt(SIZE)); + } + + public String getMessage() { + return message; + } + +} diff --git a/lagom/weather-impl/bin/application.conf b/lagom/weather-impl/bin/application.conf new file mode 100644 index 000000000000..cf6cec21151c --- /dev/null +++ b/lagom/weather-impl/bin/application.conf @@ -0,0 +1 @@ +play.modules.enabled += org.baeldung.lagom.helloworld.weather.impl.WeatherServiceModule \ No newline at end of file diff --git a/lagom/weather-impl/src/main/java/org/baeldung/lagom/helloworld/weather/impl/WeatherServiceImpl.java b/lagom/weather-impl/src/main/java/org/baeldung/lagom/helloworld/weather/impl/WeatherServiceImpl.java new file mode 100644 index 000000000000..d7f97f9105eb --- /dev/null +++ b/lagom/weather-impl/src/main/java/org/baeldung/lagom/helloworld/weather/impl/WeatherServiceImpl.java @@ -0,0 +1,19 @@ +package org.baeldung.lagom.helloworld.weather.impl; + +import java.util.concurrent.CompletableFuture; + +import org.baeldung.lagom.helloworld.weather.api.WeatherService; +import org.baeldung.lagom.helloworld.weather.api.WeatherStats; + +import com.lightbend.lagom.javadsl.api.ServiceCall; + +import akka.NotUsed; + +public class WeatherServiceImpl implements WeatherService { + + @Override + public ServiceCall weatherStatsForToday() { + return (req) -> CompletableFuture.completedFuture(WeatherStats.forToday()); + } + +} diff --git a/lagom/weather-impl/src/main/java/org/baeldung/lagom/helloworld/weather/impl/WeatherServiceModule.java b/lagom/weather-impl/src/main/java/org/baeldung/lagom/helloworld/weather/impl/WeatherServiceModule.java new file mode 100644 index 000000000000..ac2834ff5cb5 --- /dev/null +++ b/lagom/weather-impl/src/main/java/org/baeldung/lagom/helloworld/weather/impl/WeatherServiceModule.java @@ -0,0 +1,16 @@ +package org.baeldung.lagom.helloworld.weather.impl; + +import org.baeldung.lagom.helloworld.weather.api.WeatherService; + +import com.google.inject.AbstractModule; +import com.lightbend.lagom.javadsl.server.ServiceGuiceSupport; + +/** + * The module that binds the GreetingService so that it can be served. + */ +public class WeatherServiceModule extends AbstractModule implements ServiceGuiceSupport { + @Override + protected void configure() { + bindServices(serviceBinding(WeatherService.class, WeatherServiceImpl.class)); + } +} diff --git a/lagom/weather-impl/src/main/resources/application.conf b/lagom/weather-impl/src/main/resources/application.conf new file mode 100644 index 000000000000..cf6cec21151c --- /dev/null +++ b/lagom/weather-impl/src/main/resources/application.conf @@ -0,0 +1 @@ +play.modules.enabled += org.baeldung.lagom.helloworld.weather.impl.WeatherServiceModule \ No newline at end of file diff --git a/libraries/README.md b/libraries/README.md new file mode 100644 index 000000000000..1cfa7be86c39 --- /dev/null +++ b/libraries/README.md @@ -0,0 +1,18 @@ +### Relevant articles + +- [Intro to Jasypt](http://www.baeldung.com/jasypt) +- [Array Processing with Apache Commons Lang 3](http://www.baeldung.com/array-processing-commons-lang) +- [String Processing with Apache Commons Lang 3](http://www.baeldung.com/string-processing-commons-lang) +- [Introduction to Javatuples](http://www.baeldung.com/java-tuples) +- [Introduction to Javassist](http://www.baeldung.com/javassist) +- [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) + + +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. + +The code examples related to different libraries should go in a new package. + +Remember, for advanced libraries like JUnit, Jackson, etc. we already have separate modules. Please make sure to have a look at the existing modules in such cases. diff --git a/libraries/pom.xml b/libraries/pom.xml index ee93ee934f4d..0643e8257baa 100644 --- a/libraries/pom.xml +++ b/libraries/pom.xml @@ -1,6 +1,5 @@ - parent-modules @@ -21,6 +20,31 @@ 1.8
+ + org.apache.maven.plugins + maven-dependency-plugin + + + org.apache.felix + maven-bundle-plugin + 3.3.0 + maven-plugin + + + + true + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + **/*IntegrationTest.java + **/*LiveTest.java + + + @@ -31,18 +55,147 @@ cglib ${cglib.version} + + org.apache.commons + commons-lang3 + ${commons-lang.version} + junit junit ${junit.version} test + + org.jasypt + jasypt + ${jasypt.version} + + + org.javatuples + javatuples + ${javatuples.version} + + + org.javassist + javassist + ${javaassist.version} + + + + org.assertj + assertj-core + ${assertj.version} + + + org.skyscreamer + jsonassert + ${jsonassert.version} + + + org.javers + javers-core + ${javers.version} + + + org.eclipse.jetty + jetty-server + ${jetty.version} + + + org.eclipse.jetty + jetty-servlet + ${jetty.version} + + + org.apache.httpcomponents + httpclient + ${httpclient.version} + + + commons-logging + commons-logging + + + + + commons-io + commons-io + ${commons.io.version} + + + org.eclipse.jetty + jetty-server + ${jetty.version} + + + org.eclipse.jetty + jetty-servlet + ${jetty.version} + + + org.apache.httpcomponents + httpclient + ${httpclient.version} + + + commons-io + commons-io + ${commons.io.version} + + + org.apache.flink + flink-core + ${flink.version} + + + commons-logging + commons-logging + + + + + org.apache.flink + flink-java + ${flink.version} + + + commons-logging + commons-logging + + + + + org.apache.flink + flink-test-utils_2.10 + ${flink.version} + test + + + org.apache.commons + commons-math3 + 3.6.1 + 3.2.4 + 3.5 4.12 + 1.9.2 + 1.2 + 3.21.0-GA + 3.6.2 + 1.5.0 + 3.1.0 + 9.4.3.v20170317 + 4.5.3 + 2.5 + 9.4.2.v20170220 + 4.5.3 + 2.5 + 1.2.0 + 2.19.1 - \ No newline at end of file diff --git a/libraries/src/main/java/com/baeldung/flink/LineSplitter.java b/libraries/src/main/java/com/baeldung/flink/LineSplitter.java new file mode 100644 index 000000000000..8deeeb01c4ef --- /dev/null +++ b/libraries/src/main/java/com/baeldung/flink/LineSplitter.java @@ -0,0 +1,20 @@ +package com.baeldung.flink; + +import org.apache.flink.api.common.functions.FlatMapFunction; +import org.apache.flink.api.java.tuple.Tuple2; +import org.apache.flink.util.Collector; + +import java.util.stream.Stream; + +@SuppressWarnings("serial") +public class LineSplitter implements FlatMapFunction> { + + @Override + public void flatMap(String value, Collector> out) { + + String[] tokens = value.toLowerCase().split("\\W+"); + Stream.of(tokens) + .filter(t -> t.length() > 0) + .forEach(token -> out.collect(new Tuple2<>(token, 1))); + } +} \ No newline at end of file diff --git a/libraries/src/main/java/com/baeldung/flink/WordCount.java b/libraries/src/main/java/com/baeldung/flink/WordCount.java new file mode 100644 index 000000000000..ab109bdbce34 --- /dev/null +++ b/libraries/src/main/java/com/baeldung/flink/WordCount.java @@ -0,0 +1,20 @@ +package com.baeldung.flink; + +import org.apache.flink.api.java.DataSet; +import org.apache.flink.api.java.ExecutionEnvironment; +import org.apache.flink.api.java.aggregation.Aggregations; +import org.apache.flink.api.java.tuple.Tuple2; + +import java.util.List; + +public class WordCount { + + public static DataSet> startWordCount(ExecutionEnvironment env, List lines) throws Exception { + DataSet text = env.fromCollection(lines); + + return text.flatMap(new LineSplitter()) + .groupBy(0) + .aggregate(Aggregations.SUM, 1); + + } +} \ No newline at end of file diff --git a/libraries/src/main/java/com/baeldung/javasisst/Point.java b/libraries/src/main/java/com/baeldung/javasisst/Point.java new file mode 100644 index 000000000000..7e5c1cedd5cf --- /dev/null +++ b/libraries/src/main/java/com/baeldung/javasisst/Point.java @@ -0,0 +1,17 @@ +package com.baeldung.javasisst; + + +public class Point { + public int x = 0; + public int y = 0; + + public Point(int x, int y) { + this.x = x; + this.y = y; + } + + public void move(int x, int y) { + this.x = x; + this.y = y; + } +} diff --git a/libraries/src/main/java/com/baeldung/javasisst/ThreeDimensionalPoint.java b/libraries/src/main/java/com/baeldung/javasisst/ThreeDimensionalPoint.java new file mode 100644 index 000000000000..fb24d4b85d81 --- /dev/null +++ b/libraries/src/main/java/com/baeldung/javasisst/ThreeDimensionalPoint.java @@ -0,0 +1,19 @@ +package com.baeldung.javasisst; + + +public class ThreeDimensionalPoint { + public int x = 0; + public int y = 0; + public int z = 0; + + public ThreeDimensionalPoint(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + } + + public void move(int x, int y) { + this.x = x; + this.y = y; + } +} diff --git a/libraries/src/main/java/com/baeldung/javers/Address.java b/libraries/src/main/java/com/baeldung/javers/Address.java new file mode 100644 index 000000000000..14f5907ef6f2 --- /dev/null +++ b/libraries/src/main/java/com/baeldung/javers/Address.java @@ -0,0 +1,11 @@ +package com.baeldung.javers; + + +public class Address { + private String country; + + public Address(String country) { + this.country = country; + } + +} diff --git a/libraries/src/main/java/com/baeldung/javers/Person.java b/libraries/src/main/java/com/baeldung/javers/Person.java new file mode 100644 index 000000000000..c53a09358b9b --- /dev/null +++ b/libraries/src/main/java/com/baeldung/javers/Person.java @@ -0,0 +1,27 @@ +package com.baeldung.javers; + +public class Person { + private Integer id; + private String name; + + public Person(Integer id, String name) { + this.id = id; + this.name = name; + } + + public Integer getId() { + return id; + } + + public String getName() { + return name; + } + + public void setId(Integer id) { + this.id = id; + } + + public void setName(String name) { + this.name = name; + } +} \ No newline at end of file diff --git a/libraries/src/main/java/com/baeldung/javers/PersonWithAddress.java b/libraries/src/main/java/com/baeldung/javers/PersonWithAddress.java new file mode 100644 index 000000000000..0b4e33fcb5c6 --- /dev/null +++ b/libraries/src/main/java/com/baeldung/javers/PersonWithAddress.java @@ -0,0 +1,40 @@ +package com.baeldung.javers; + + +import java.util.List; + +public class PersonWithAddress { + private Integer id; + private String name; + private List
address; + + public PersonWithAddress(Integer id, String name, List
address) { + this.id = id; + this.name = name; + this.address = address; + } + + public Integer getId() { + return id; + } + + public String getName() { + return name; + } + + public void setId(Integer id) { + this.id = id; + } + + public void setName(String name) { + this.name = name; + } + + public List
getAddress() { + return address; + } + + public void setAddress(List
address) { + this.address = address; + } +} diff --git a/libraries/src/main/java/com/baeldung/jetty/AsyncServlet.java b/libraries/src/main/java/com/baeldung/jetty/AsyncServlet.java new file mode 100644 index 000000000000..d1bddd097fa9 --- /dev/null +++ b/libraries/src/main/java/com/baeldung/jetty/AsyncServlet.java @@ -0,0 +1,42 @@ +package com.baeldung.jetty; + +import javax.servlet.AsyncContext; +import javax.servlet.ServletException; +import javax.servlet.ServletOutputStream; +import javax.servlet.WriteListener; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; + +public class AsyncServlet extends HttpServlet { + private static final String HEAVY_RESOURCE = "This is some heavy resource that will be served in an async way"; + + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + ByteBuffer content = ByteBuffer.wrap(HEAVY_RESOURCE.getBytes(StandardCharsets.UTF_8)); + + AsyncContext async = request.startAsync(); + ServletOutputStream out = response.getOutputStream(); + out.setWriteListener(new WriteListener() { + @Override + public void onWritePossible() throws IOException { + while (out.isReady()) { + if (!content.hasRemaining()) { + response.setStatus(200); + async.complete(); + return; + } + out.write(content.get()); + } + } + + @Override + public void onError(Throwable t) { + getServletContext().log("Async Error", t); + async.complete(); + } + }); + } +} \ No newline at end of file diff --git a/libraries/src/main/java/com/baeldung/jetty/BlockingServlet.java b/libraries/src/main/java/com/baeldung/jetty/BlockingServlet.java new file mode 100644 index 000000000000..f1de71beeb61 --- /dev/null +++ b/libraries/src/main/java/com/baeldung/jetty/BlockingServlet.java @@ -0,0 +1,17 @@ +package com.baeldung.jetty; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +public class BlockingServlet extends HttpServlet { + + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("application/json"); + response.setStatus(HttpServletResponse.SC_OK); + response.getWriter().println("{ \"status\": \"ok\"}"); + } +} + diff --git a/libraries/src/main/java/com/baeldung/jetty/JettyServer.java b/libraries/src/main/java/com/baeldung/jetty/JettyServer.java new file mode 100644 index 000000000000..1033a7266dbd --- /dev/null +++ b/libraries/src/main/java/com/baeldung/jetty/JettyServer.java @@ -0,0 +1,39 @@ +package com.baeldung.jetty; + +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.servlet.ServletHandler; +import org.eclipse.jetty.util.thread.QueuedThreadPool; + +public class JettyServer { + + private Server server; + + public void start() throws Exception { + + int maxThreads = 100; + int minThreads = 10; + int idleTimeout = 120; + + QueuedThreadPool threadPool = new QueuedThreadPool(maxThreads, minThreads, idleTimeout); + + server = new Server(threadPool); + ServerConnector connector = new ServerConnector(server); + connector.setPort(8090); + server.setConnectors(new Connector[]{connector}); + + ServletHandler servletHandler = new ServletHandler(); + server.setHandler(servletHandler); + + servletHandler.addServletWithMapping(BlockingServlet.class, "/status"); + servletHandler.addServletWithMapping(AsyncServlet.class, "/heavy/async"); + + server.start(); + + } + + public void stop() throws Exception { + server.stop(); + } +} diff --git a/libraries/src/test/java/com/baeldung/cglib/proxy/README.md b/libraries/src/test/java/com/baeldung/cglib/proxy/README.md new file mode 100644 index 000000000000..abeabc6162f0 --- /dev/null +++ b/libraries/src/test/java/com/baeldung/cglib/proxy/README.md @@ -0,0 +1,3 @@ +### Relevant articles + +- [Introduction to cglib](http://www.baeldung.com/cglib) diff --git a/libraries/src/test/java/com/baeldung/commons/lang3/ArrayUtilsTest.java b/libraries/src/test/java/com/baeldung/commons/lang3/ArrayUtilsTest.java new file mode 100644 index 000000000000..b57945873008 --- /dev/null +++ b/libraries/src/test/java/com/baeldung/commons/lang3/ArrayUtilsTest.java @@ -0,0 +1,137 @@ +package com.baeldung.commons.lang3; + +import org.apache.commons.lang3.ArrayUtils; +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertArrayEquals; + +public class ArrayUtilsTest { + @Test + public void givenArray_whenAddingElementAtSpecifiedPosition_thenCorrect() { + int[] oldArray = { 2, 3, 4, 5 }; + int[] newArray = ArrayUtils.add(oldArray, 0, 1); + int[] expectedArray = { 1, 2, 3, 4, 5 }; + assertArrayEquals(expectedArray, newArray); + } + + @Test + public void givenArray_whenAddingElementAtTheEnd_thenCorrect() { + int[] oldArray = { 2, 3, 4, 5 }; + int[] newArray = ArrayUtils.add(oldArray, 1); + int[] expectedArray = { 2, 3, 4, 5, 1 }; + assertArrayEquals(expectedArray, newArray); + } + + @Test + public void givenArray_whenAddingAllElementsAtTheEnd_thenCorrect() { + int[] oldArray = { 0, 1, 2 }; + int[] newArray = ArrayUtils.addAll(oldArray, 3, 4, 5); + int[] expectedArray = { 0, 1, 2, 3, 4, 5 }; + assertArrayEquals(expectedArray, newArray); + } + + @Test + public void givenArray_whenRemovingElementAtSpecifiedPosition_thenCorrect() { + int[] oldArray = { 1, 2, 3, 4, 5 }; + int[] newArray = ArrayUtils.remove(oldArray, 1); + int[] expectedArray = { 1, 3, 4, 5 }; + assertArrayEquals(expectedArray, newArray); + } + + @Test + public void givenArray_whenRemovingAllElementsAtSpecifiedPositions_thenCorrect() { + int[] oldArray = { 1, 2, 3, 4, 5 }; + int[] newArray = ArrayUtils.removeAll(oldArray, 1, 3); + int[] expectedArray = { 1, 3, 5 }; + assertArrayEquals(expectedArray, newArray); + } + + @Test + public void givenArray_whenRemovingAnElement_thenCorrect() { + int[] oldArray = { 1, 2, 3, 3, 4 }; + int[] newArray = ArrayUtils.removeElement(oldArray, 3); + int[] expectedArray = { 1, 2, 3, 4 }; + assertArrayEquals(expectedArray, newArray); + } + + @Test + public void givenArray_whenRemovingElements_thenCorrect() { + int[] oldArray = { 1, 2, 3, 3, 4 }; + int[] newArray = ArrayUtils.removeElements(oldArray, 2, 3, 5); + int[] expectedArray = { 1, 3, 4 }; + assertArrayEquals(expectedArray, newArray); + } + + @Test + public void givenArray_whenRemovingAllElementOccurences_thenCorrect() { + int[] oldArray = { 1, 2, 2, 2, 3 }; + int[] newArray = ArrayUtils.removeAllOccurences(oldArray, 2); + int[] expectedArray = { 1, 3 }; + assertArrayEquals(expectedArray, newArray); + } + + @Test + public void givenArray_whenCheckingExistingElement_thenCorrect() { + int[] array = { 1, 3, 5, 7, 9 }; + boolean evenContained = ArrayUtils.contains(array, 2); + boolean oddContained = ArrayUtils.contains(array, 7); + assertEquals(false, evenContained); + assertEquals(true, oddContained); + } + + @Test + public void givenArray_whenReversingElementsWithinARange_thenCorrect() { + int[] originalArray = { 1, 2, 3, 4, 5 }; + ArrayUtils.reverse(originalArray, 1, 4); + int[] expectedArray = { 1, 4, 3, 2, 5 }; + assertArrayEquals(expectedArray, originalArray); + } + + @Test + public void givenArray_whenReversingAllElements_thenCorrect() { + int[] originalArray = { 1, 2, 3, 4, 5 }; + ArrayUtils.reverse(originalArray); + int[] expectedArray = { 5, 4, 3, 2, 1 }; + assertArrayEquals(expectedArray, originalArray); + } + + @Test + public void givenArray_whenShiftingElementsWithinARange_thenCorrect() { + int[] originalArray = { 1, 2, 3, 4, 5 }; + ArrayUtils.shift(originalArray, 1, 4, 1); + int[] expectedArray = { 1, 4, 2, 3, 5 }; + assertArrayEquals(expectedArray, originalArray); + } + + @Test + public void givenArray_whenShiftingAllElements_thenCorrect() { + int[] originalArray = { 1, 2, 3, 4, 5 }; + ArrayUtils.shift(originalArray, 1); + int[] expectedArray = { 5, 1, 2, 3, 4 }; + assertArrayEquals(expectedArray, originalArray); + } + + @Test + public void givenArray_whenExtractingElements_thenCorrect() { + int[] oldArray = { 1, 2, 3, 4, 5 }; + int[] newArray = ArrayUtils.subarray(oldArray, 2, 7); + int[] expectedArray = { 3, 4, 5 }; + assertArrayEquals(expectedArray, newArray); + } + + @Test + public void givenArray_whenSwapingElementsWithinARange_thenCorrect() { + int[] originalArray = { 1, 2, 3, 4, 5 }; + ArrayUtils.swap(originalArray, 0, 3, 2); + int[] expectedArray = { 4, 5, 3, 1, 2 }; + assertArrayEquals(expectedArray, originalArray); + } + + @Test + public void givenArray_whenSwapingElementsAtSpecifiedPositions_thenCorrect() { + int[] originalArray = { 1, 2, 3, 4, 5 }; + ArrayUtils.swap(originalArray, 0, 3); + int[] expectedArray = { 4, 2, 3, 1, 5 }; + assertArrayEquals(expectedArray, originalArray); + } +} diff --git a/libraries/src/test/java/com/baeldung/commons/lang3/StringUtilsTest.java b/libraries/src/test/java/com/baeldung/commons/lang3/StringUtilsTest.java new file mode 100644 index 000000000000..a17a8ea60db8 --- /dev/null +++ b/libraries/src/test/java/com/baeldung/commons/lang3/StringUtilsTest.java @@ -0,0 +1,99 @@ +package com.baeldung.commons.lang3; + +import org.apache.commons.lang3.StringUtils; +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; + +public class StringUtilsTest { + @Test + public void givenString_whenCheckingContainsAny_thenCorrect() { + String string = "baeldung.com"; + boolean contained1 = StringUtils.containsAny(string, 'a', 'b', 'c'); + boolean contained2 = StringUtils.containsAny(string, 'x', 'y', 'z'); + boolean contained3 = StringUtils.containsAny(string, "abc"); + boolean contained4 = StringUtils.containsAny(string, "xyz"); + assertTrue(contained1); + assertFalse(contained2); + assertTrue(contained3); + assertFalse(contained4); + } + + @Test + public void givenString_whenCheckingContainsIgnoreCase_thenCorrect() { + String string = "baeldung.com"; + boolean contained = StringUtils.containsIgnoreCase(string, "BAELDUNG"); + assertTrue(contained); + } + + @Test + public void givenString_whenCountingMatches_thenCorrect() { + String string = "welcome to www.baeldung.com"; + int charNum = StringUtils.countMatches(string, 'w'); + int stringNum = StringUtils.countMatches(string, "com"); + assertEquals(4, charNum); + assertEquals(2, stringNum); + } + + @Test + public void givenString_whenAppendingAndPrependingIfMissing_thenCorrect() { + String string = "baeldung.com"; + String stringWithSuffix = StringUtils.appendIfMissing(string, ".com"); + String stringWithPrefix = StringUtils.prependIfMissing(string, "www."); + assertEquals("baeldung.com", stringWithSuffix); + assertEquals("www.baeldung.com", stringWithPrefix); + } + + @Test + public void givenString_whenSwappingCase_thenCorrect() { + String originalString = "baeldung.COM"; + String swappedString = StringUtils.swapCase(originalString); + assertEquals("BAELDUNG.com", swappedString); + } + + @Test + public void givenString_whenCapitalizing_thenCorrect() { + String originalString = "baeldung"; + String capitalizedString = StringUtils.capitalize(originalString); + assertEquals("Baeldung", capitalizedString); + } + + @Test + public void givenString_whenUncapitalizing_thenCorrect() { + String originalString = "Baeldung"; + String uncapitalizedString = StringUtils.uncapitalize(originalString); + assertEquals("baeldung", uncapitalizedString); + } + + @Test + public void givenString_whenReversingCharacters_thenCorrect() { + String originalString = "baeldung"; + String reversedString = StringUtils.reverse(originalString); + assertEquals("gnudleab", reversedString); + } + + @Test + public void givenString_whenReversingWithDelimiter_thenCorrect() { + String originalString = "www.baeldung.com"; + String reversedString = StringUtils.reverseDelimited(originalString, '.'); + assertEquals("com.baeldung.www", reversedString); + } + + @Test + public void givenString_whenRotatingTwoPositions_thenCorrect() { + String originalString = "baeldung"; + String rotatedString = StringUtils.rotate(originalString, 4); + assertEquals("dungbael", rotatedString); + } + + @Test + public void givenTwoStrings_whenComparing_thenCorrect() { + String tutorials = "Baeldung Tutorials"; + String courses = "Baeldung Courses"; + String diff1 = StringUtils.difference(tutorials, courses); + String diff2 = StringUtils.difference(courses, tutorials); + assertEquals("Courses", diff1); + assertEquals("Tutorials", diff2); + } +} diff --git a/libraries/src/test/java/com/baeldung/commons/math/ComplexTests.java b/libraries/src/test/java/com/baeldung/commons/math/ComplexTests.java new file mode 100644 index 000000000000..2e5074453e1c --- /dev/null +++ b/libraries/src/test/java/com/baeldung/commons/math/ComplexTests.java @@ -0,0 +1,20 @@ +package com.baeldung.commons.math; + +import org.apache.commons.math3.complex.Complex; +import org.junit.Assert; +import org.junit.Test; + +public class ComplexTests { + + @Test + public void whenComplexPow_thenCorrect() { + Complex first = new Complex(1.0, 3.0); + Complex second = new Complex(2.0, 5.0); + + Complex power = first.pow(second); + + Assert.assertEquals(-0.007563724861696302, power.getReal(), 1e-7); + Assert.assertEquals(0.01786136835085382, power.getImaginary(), 1e-7); + } + +} diff --git a/libraries/src/test/java/com/baeldung/commons/math/FractionTests.java b/libraries/src/test/java/com/baeldung/commons/math/FractionTests.java new file mode 100644 index 000000000000..6828c8490c18 --- /dev/null +++ b/libraries/src/test/java/com/baeldung/commons/math/FractionTests.java @@ -0,0 +1,19 @@ +package com.baeldung.commons.math; + +import org.apache.commons.math3.fraction.Fraction; +import org.junit.Assert; +import org.junit.Test; + +public class FractionTests { + + @Test + public void whenFractionAdd_thenCorrect() { + Fraction lhs = new Fraction(1, 3); + Fraction rhs = new Fraction(2, 5); + Fraction sum = lhs.add(rhs); + + Assert.assertEquals(11, sum.getNumerator()); + Assert.assertEquals(15, sum.getDenominator()); + } + +} diff --git a/libraries/src/test/java/com/baeldung/commons/math/GeometryTests.java b/libraries/src/test/java/com/baeldung/commons/math/GeometryTests.java new file mode 100644 index 000000000000..17cbb607d66e --- /dev/null +++ b/libraries/src/test/java/com/baeldung/commons/math/GeometryTests.java @@ -0,0 +1,21 @@ +package com.baeldung.commons.math; + +import org.apache.commons.math3.geometry.euclidean.twod.Line; +import org.apache.commons.math3.geometry.euclidean.twod.Vector2D; +import org.junit.Assert; +import org.junit.Test; + +public class GeometryTests { + + @Test + public void whenLineIntersection_thenCorrect() { + Line l1 = new Line(new Vector2D(0, 0), new Vector2D(1, 1), 0); + Line l2 = new Line(new Vector2D(0, 1), new Vector2D(1, 1.5), 0); + + Vector2D intersection = l1.intersection(l2); + + Assert.assertEquals(2, intersection.getX(), 1e-7); + Assert.assertEquals(2, intersection.getY(), 1e-7); + } + +} diff --git a/libraries/src/test/java/com/baeldung/commons/math/IntegrationTests.java b/libraries/src/test/java/com/baeldung/commons/math/IntegrationTests.java new file mode 100644 index 000000000000..62ae898fa87a --- /dev/null +++ b/libraries/src/test/java/com/baeldung/commons/math/IntegrationTests.java @@ -0,0 +1,21 @@ +package com.baeldung.commons.math; + +import org.apache.commons.math3.analysis.UnivariateFunction; +import org.apache.commons.math3.analysis.integration.SimpsonIntegrator; +import org.apache.commons.math3.analysis.integration.UnivariateIntegrator; +import org.junit.Assert; +import org.junit.Test; + +public class IntegrationTests { + + @Test + public void whenUnivariateIntegratorIntegrate_thenCorrect() { + final UnivariateFunction function = v -> v; + final UnivariateIntegrator integrator = new SimpsonIntegrator(1.0e-12, 1.0e-8, 1, 32); + + final double i = integrator.integrate(100, function, 0, 10); + + Assert.assertEquals(16 + 2d/3d, i, 1e-7); + } + +} diff --git a/libraries/src/test/java/com/baeldung/commons/math/LinearAlgebraTests.java b/libraries/src/test/java/com/baeldung/commons/math/LinearAlgebraTests.java new file mode 100644 index 000000000000..981f839280c5 --- /dev/null +++ b/libraries/src/test/java/com/baeldung/commons/math/LinearAlgebraTests.java @@ -0,0 +1,25 @@ +package com.baeldung.commons.math; + +import org.apache.commons.math3.linear.*; +import org.junit.Assert; +import org.junit.Test; + +public class LinearAlgebraTests { + + @Test + public void whenDecompositionSolverSolve_thenCorrect() { + RealMatrix a = + new Array2DRowRealMatrix(new double[][] { { 2, 3, -2 }, { -1, 7, 6 }, { 4, -3, -5 } }, + false); + RealVector b = new ArrayRealVector(new double[] { 1, -2, 1 }, false); + + DecompositionSolver solver = new LUDecomposition(a).getSolver(); + + RealVector solution = solver.solve(b); + + Assert.assertEquals(-0.3698630137, solution.getEntry(0), 1e-7); + Assert.assertEquals(0.1780821918, solution.getEntry(1), 1e-7); + Assert.assertEquals(-0.602739726, solution.getEntry(2), 1e-7); + } + +} diff --git a/libraries/src/test/java/com/baeldung/commons/math/ProbabilitiesTests.java b/libraries/src/test/java/com/baeldung/commons/math/ProbabilitiesTests.java new file mode 100644 index 000000000000..41ccb16c5a37 --- /dev/null +++ b/libraries/src/test/java/com/baeldung/commons/math/ProbabilitiesTests.java @@ -0,0 +1,15 @@ +package com.baeldung.commons.math; + +import org.apache.commons.math3.distribution.NormalDistribution; +import org.junit.Test; + +public class ProbabilitiesTests { + + @Test + public void whenNormalDistributionSample_thenSuccess() { + final NormalDistribution normalDistribution = new NormalDistribution(10, 3); + + System.out.println(normalDistribution.sample()); + } + +} diff --git a/libraries/src/test/java/com/baeldung/commons/math/RootFindingTests.java b/libraries/src/test/java/com/baeldung/commons/math/RootFindingTests.java new file mode 100644 index 000000000000..3e6b542c0131 --- /dev/null +++ b/libraries/src/test/java/com/baeldung/commons/math/RootFindingTests.java @@ -0,0 +1,20 @@ +package com.baeldung.commons.math; + +import org.apache.commons.math3.analysis.UnivariateFunction; +import org.apache.commons.math3.analysis.solvers.BracketingNthOrderBrentSolver; +import org.apache.commons.math3.analysis.solvers.UnivariateSolver; +import org.junit.Assert; +import org.junit.Test; + +public class RootFindingTests { + + @Test + public void whenUnivariateSolverSolver_thenCorrect() { + final UnivariateFunction function = v -> Math.pow(v, 2) - 2; + UnivariateSolver solver = new BracketingNthOrderBrentSolver(1.0e-12, 1.0e-8, 5); + double c = solver.solve(100, function, -10.0, 10.0, 0); + + Assert.assertEquals(-Math.sqrt(2), c, 1e-7); + } + +} diff --git a/libraries/src/test/java/com/baeldung/commons/math/StatisticsTests.java b/libraries/src/test/java/com/baeldung/commons/math/StatisticsTests.java new file mode 100644 index 000000000000..c1b0619b11ed --- /dev/null +++ b/libraries/src/test/java/com/baeldung/commons/math/StatisticsTests.java @@ -0,0 +1,38 @@ +package com.baeldung.commons.math; + +import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class StatisticsTests { + + private double[] values; + private DescriptiveStatistics descriptiveStatistics; + + @Before + public void setUp() { + values = new double[] {65, 51 , 16, 11 , 6519, 191 ,0 , 98, 19854, 1, 32}; + + descriptiveStatistics = new DescriptiveStatistics(); + for(double v : values) { + descriptiveStatistics.addValue(v); + } + } + + @Test + public void whenDescriptiveStatisticsGetMean_thenCorrect() { + Assert.assertEquals(2439.8181818181815, descriptiveStatistics.getMean(), 1e-7); + } + + @Test + public void whenDescriptiveStatisticsGetMedian_thenCorrect() { + Assert.assertEquals(51, descriptiveStatistics.getPercentile(50), 1e-7); + } + + @Test + public void whenDescriptiveStatisticsGetStandardDeviation_thenCorrect() { + Assert.assertEquals(6093.054649651221, descriptiveStatistics.getStandardDeviation(), 1e-7); + } + +} diff --git a/libraries/src/test/java/com/baeldung/flink/WordCountIntegrationTest.java b/libraries/src/test/java/com/baeldung/flink/WordCountIntegrationTest.java new file mode 100644 index 000000000000..a5613e2406ec --- /dev/null +++ b/libraries/src/test/java/com/baeldung/flink/WordCountIntegrationTest.java @@ -0,0 +1,186 @@ +package com.baeldung.flink; + +import org.apache.flink.api.common.operators.Order; +import org.apache.flink.api.java.DataSet; +import org.apache.flink.api.java.ExecutionEnvironment; +import org.apache.flink.api.java.functions.KeySelector; +import org.apache.flink.api.java.tuple.Tuple2; +import org.apache.flink.api.java.tuple.Tuple3; +import org.apache.flink.streaming.api.datastream.DataStream; +import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator; +import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; +import org.apache.flink.streaming.api.functions.timestamps.BoundedOutOfOrdernessTimestampExtractor; +import org.apache.flink.streaming.api.windowing.assigners.TumblingEventTimeWindows; +import org.apache.flink.streaming.api.windowing.time.Time; +import org.junit.Test; + +import java.time.ZonedDateTime; +import java.util.Arrays; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + + +public class WordCountIntegrationTest { + private final ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment(); + + @Test + public void givenDataSet_whenExecuteWordCount_thenReturnWordCount() throws Exception { + //given + List lines = Arrays.asList("This is a first sentence", "This is a second sentence with a one word"); + + //when + DataSet> result = WordCount.startWordCount(env, lines); + + //then + List> collect = result.collect(); + assertThat(collect).containsExactlyInAnyOrder( + new Tuple2<>("a", 3), new Tuple2<>("sentence", 2), new Tuple2<>("word", 1), + new Tuple2<>("is", 2), new Tuple2<>("this", 2), new Tuple2<>("second", 1), + new Tuple2<>("first", 1), new Tuple2<>("with", 1), new Tuple2<>("one", 1)); + } + + @Test + public void givenListOfAmounts_whenUseMapReduce_thenSumAmountsThatAreOnlyAboveThreshold() throws Exception { + //given + DataSet amounts = env.fromElements(1, 29, 40, 50); + int threshold = 30; + + //when + List collect = amounts + .filter(a -> a > threshold) + .reduce((integer, t1) -> integer + t1) + .collect(); + + //then + assertThat(collect.get(0)).isEqualTo(90); + } + + @Test + public void givenDataSetOfComplexObjects_whenMapToGetOneField_thenReturnedListHaveProperElements() throws Exception { + //given + DataSet personDataSource = env.fromCollection(Arrays.asList(new Person(23, "Tom"), new Person(75, "Michael"))); + + //when + List ages = personDataSource.map(p -> p.age).collect(); + + //then + assertThat(ages).hasSize(2); + assertThat(ages).contains(23, 75); + + } + + @Test + public void givenDataSet_whenSortItByOneField_thenShouldReturnSortedDataSet() throws Exception { + //given + Tuple2 secondPerson = new Tuple2<>(4, "Tom"); + Tuple2 thirdPerson = new Tuple2<>(5, "Scott"); + Tuple2 fourthPerson = new Tuple2<>(200, "Michael"); + Tuple2 firstPerson = new Tuple2<>(1, "Jack"); + DataSet> transactions = env.fromElements(fourthPerson, secondPerson, + thirdPerson, firstPerson); + + + //when + List> sorted = transactions + .sortPartition(new IdKeySelectorTransaction(), Order.ASCENDING) + .collect(); + + //then + assertThat(sorted).containsExactly(firstPerson, secondPerson, thirdPerson, fourthPerson); + } + + + @Test + public void giveTwoDataSets_whenJoinUsingId_thenProduceJoinedData() throws Exception { + //given + Tuple3 address = new Tuple3<>(1, "5th Avenue", "London"); + DataSet> addresses = env.fromElements(address); + + Tuple2 firstTransaction = new Tuple2<>(1, "Transaction_1"); + DataSet> transactions = + env.fromElements(firstTransaction, new Tuple2<>(12, "Transaction_2")); + + + //when + List, Tuple3>> joined = + transactions.join(addresses) + .where(new IdKeySelectorTransaction()) + .equalTo(new IdKeySelectorAddress()) + .collect(); + + //then + assertThat(joined).hasSize(1); + assertThat(joined).contains(new Tuple2<>(firstTransaction, address)); + + } + + @Test + public void givenStreamOfEvents_whenProcessEvents_thenShouldPrintResultsOnSinkOperation() throws Exception { + //given + final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); + + DataStream text + = env.fromElements("This is a first sentence", "This is a second sentence with a one word"); + + + SingleOutputStreamOperator upperCase = text.map(String::toUpperCase); + + upperCase.print(); + + //when + env.execute(); + } + + + @Test + public void givenStreamOfEvents_whenProcessEvents_thenShouldApplyWindowingOnTransformation() throws Exception { + //given + final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); + + SingleOutputStreamOperator> windowed = env.fromElements( + new Tuple2<>(16, ZonedDateTime.now().plusMinutes(25).toInstant().getEpochSecond()), + new Tuple2<>(15, ZonedDateTime.now().plusMinutes(2).toInstant().getEpochSecond()) + ).assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor>(Time.seconds(20)) { + @Override + public long extractTimestamp(Tuple2 element) { + return element.f1 * 1000; + } + }); + + SingleOutputStreamOperator> reduced = windowed + .windowAll(TumblingEventTimeWindows.of(Time.seconds(5))) + .maxBy(0, true); + + reduced.print(); + + //when + env.execute(); + } + + + private static class IdKeySelectorTransaction implements KeySelector, Integer> { + @Override + public Integer getKey(Tuple2 value) { + return value.f0; + } + } + + private static class IdKeySelectorAddress implements KeySelector, Integer> { + @Override + public Integer getKey(Tuple3 value) { + return value.f0; + } + } + + private static class Person { + private final int age; + private final String name; + + private Person(int age, String name) { + this.age = age; + this.name = name; + } + } + +} \ No newline at end of file diff --git a/libraries/src/test/java/com/baeldung/jasypt/JasyptTest.java b/libraries/src/test/java/com/baeldung/jasypt/JasyptTest.java new file mode 100644 index 000000000000..fbfa7f823d8d --- /dev/null +++ b/libraries/src/test/java/com/baeldung/jasypt/JasyptTest.java @@ -0,0 +1,96 @@ +package com.baeldung.jasypt; + + +import org.jasypt.encryption.pbe.PooledPBEStringEncryptor; +import org.jasypt.encryption.pbe.StandardPBEStringEncryptor; +import org.jasypt.util.password.BasicPasswordEncryptor; +import org.jasypt.util.text.BasicTextEncryptor; +import org.junit.Ignore; +import org.junit.Test; + +import static junit.framework.Assert.*; +import static junit.framework.TestCase.assertEquals; + +public class JasyptTest { + + @Test + public void givenTextPrivateData_whenDecrypt_thenCompareToEncrypted() { + //given + BasicTextEncryptor textEncryptor = new BasicTextEncryptor(); + String privateData = "secret-data"; + textEncryptor.setPasswordCharArray("some-random-data".toCharArray()); + + //when + String myEncryptedText = textEncryptor.encrypt(privateData); + assertNotSame(privateData, myEncryptedText); //myEncryptedText can be save in db + + //then + String plainText = textEncryptor.decrypt(myEncryptedText); + assertEquals(plainText, privateData); + } + + @Test + public void givenTextPassword_whenOneWayEncryption_thenCompareEncryptedPasswordsShouldBeSame(){ + String password = "secret-pass"; + BasicPasswordEncryptor passwordEncryptor = new BasicPasswordEncryptor(); + String encryptedPassword = passwordEncryptor.encryptPassword(password); + + //when + boolean result = passwordEncryptor.checkPassword("secret-pass", encryptedPassword); + + //then + assertTrue(result); + } + + @Test + public void givenTextPassword_whenOneWayEncryption_thenCompareEncryptedPasswordsShouldNotBeSame(){ + String password = "secret-pass"; + BasicPasswordEncryptor passwordEncryptor = new BasicPasswordEncryptor(); + String encryptedPassword = passwordEncryptor.encryptPassword(password); + + //when + boolean result = passwordEncryptor.checkPassword("secret-pass-not-same", encryptedPassword); + + //then + assertFalse(result); + } + + + + @Test + @Ignore("should have installed local_policy.jar") + public void givenTextPrivateData_whenDecrypt_thenCompareToEncryptedWithCustomAlgorithm() { + //given + StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor(); + String privateData = "secret-data"; + encryptor.setPassword("some-random-data"); + encryptor.setAlgorithm("PBEWithMD5AndTripleDES"); + + //when + String encryptedText = encryptor.encrypt("secret-pass"); + assertNotSame(privateData, encryptedText); + + //then + String plainText = encryptor.decrypt(encryptedText); + assertEquals(plainText, privateData); + } + + @Test + @Ignore("should have installed local_policy.jar") + public void givenTextPrivateData_whenDecryptOnHighPerformance_thenDecrypt(){ + //given + String privateData = "secret-data"; + PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor(); + encryptor.setPoolSize(4); + encryptor.setPassword("some-random-data"); + encryptor.setAlgorithm("PBEWithMD5AndTripleDES"); + + //when + String encryptedText = encryptor.encrypt(privateData); + assertNotSame(privateData, encryptedText); + + //then + String plainText = encryptor.decrypt(encryptedText); + assertEquals(plainText, privateData); + } +} diff --git a/libraries/src/test/java/com/baeldung/javassist/JavasisstTest.java b/libraries/src/test/java/com/baeldung/javassist/JavasisstTest.java new file mode 100644 index 000000000000..da5ae02c568f --- /dev/null +++ b/libraries/src/test/java/com/baeldung/javassist/JavasisstTest.java @@ -0,0 +1,119 @@ +package com.baeldung.javassist; + + +import javassist.CannotCompileException; +import javassist.ClassPool; +import javassist.NotFoundException; +import javassist.bytecode.*; +import org.junit.Test; + +import java.io.DataOutputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class JavasisstTest { + @Test + public void givenJavasisstAPI_whenConstructClass_thenGenerateAClassFile() throws CannotCompileException, IOException, ClassNotFoundException, IllegalAccessException, InstantiationException { + //given + String classNameWithPackage = "com.baeldung.JavassistGeneratedClass"; + ClassFile cf = new ClassFile(false, classNameWithPackage, null); + cf.setInterfaces(new String[]{"java.lang.Cloneable"}); + + FieldInfo f = new FieldInfo(cf.getConstPool(), "id", "I"); + f.setAccessFlags(AccessFlag.PUBLIC); + cf.addField(f); + + //when + String className = "JavassistGeneratedClass.class"; + cf.write(new DataOutputStream(new FileOutputStream(className))); + + //then + ClassPool classPool = ClassPool.getDefault(); + Field[] fields = classPool.makeClass(cf).toClass().getFields(); + assertEquals(fields[0].getName(), "id"); + + String classContent = new String(Files.readAllBytes(Paths.get(className))); + assertTrue(classContent.contains("java/lang/Cloneable")); + } + + @Test + public void givenJavaClass_whenLoadAtByJavassist_thenTraversWholeClass() throws NotFoundException, CannotCompileException, BadBytecode { + //given + ClassPool cp = ClassPool.getDefault(); + ClassFile cf = cp.get("com.baeldung.javasisst.Point").getClassFile(); + MethodInfo minfo = cf.getMethod("move"); + CodeAttribute ca = minfo.getCodeAttribute(); + CodeIterator ci = ca.iterator(); + + //when + List operations = new LinkedList<>(); + while (ci.hasNext()) { + int index = ci.next(); + int op = ci.byteAt(index); + operations.add(Mnemonic.OPCODE[op]); + } + + //then + assertEquals(operations, + Arrays.asList("aload_0", "iload_1", "putfield", "aload_0", "iload_2", "putfield", "return")); + + } + + @Test + public void givenTableOfInstructions_whenAddNewInstruction_thenShouldConstructProperSequence() throws NotFoundException, BadBytecode, CannotCompileException, IllegalAccessException, InstantiationException { + //given + ClassFile cf = ClassPool.getDefault().get("com.baeldung.javasisst.ThreeDimensionalPoint").getClassFile(); + + //when + FieldInfo f = new FieldInfo(cf.getConstPool(), "id", "I"); + f.setAccessFlags(AccessFlag.PUBLIC); + cf.addField(f); + + + ClassPool classPool = ClassPool.getDefault(); + Field[] fields = classPool.makeClass(cf).toClass().getFields(); + List fieldsList = Stream.of(fields).map(Field::getName).collect(Collectors.toList()); + assertTrue(fieldsList.contains("id")); + + } + + @Test + public void givenLoadedClass_whenAddConstructorToClass_shouldCreateClassWithConstructor() throws NotFoundException, CannotCompileException, BadBytecode { + //given + ClassFile cf = ClassPool.getDefault().get("com.baeldung.javasisst.Point").getClassFile(); + Bytecode code = new Bytecode(cf.getConstPool()); + code.addAload(0); + code.addInvokespecial("java/lang/Object", MethodInfo.nameInit, "()V"); + code.addReturn(null); + + //when + MethodInfo minfo = new MethodInfo(cf.getConstPool(), MethodInfo.nameInit, "()V"); + minfo.setCodeAttribute(code.toCodeAttribute()); + cf.addMethod(minfo); + + //then + CodeIterator ci = code.toCodeAttribute().iterator(); + List operations = new LinkedList<>(); + while (ci.hasNext()) { + int index = ci.next(); + int op = ci.byteAt(index); + operations.add(Mnemonic.OPCODE[op]); + } + + assertEquals(operations, + Arrays.asList("aload_0", "invokespecial", "return")); + + + } +} diff --git a/libraries/src/test/java/com/baeldung/javatuples/JavaTuplesTest.java b/libraries/src/test/java/com/baeldung/javatuples/JavaTuplesTest.java new file mode 100644 index 000000000000..af4308e188d6 --- /dev/null +++ b/libraries/src/test/java/com/baeldung/javatuples/JavaTuplesTest.java @@ -0,0 +1,118 @@ +package com.baeldung.javatuples; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Arrays; +import java.util.List; + +import org.javatuples.KeyValue; +import org.javatuples.LabelValue; +import org.javatuples.Pair; +import org.javatuples.Quartet; +import org.javatuples.Triplet; +import org.javatuples.Unit; +import org.junit.Test; + +public class JavaTuplesTest { + + @SuppressWarnings("unused") + @Test + public void whenCreatingTuples_thenCreateTuples() { + Pair pair = new Pair("This is a pair", 55); + Triplet triplet = Triplet.with("hello", 23, 33.2); + + List collectionOfNames = Arrays.asList("john", "doe", "anne", "alex"); + Quartet quartet = Quartet.fromCollection(collectionOfNames); + + Pair pairFromList = Pair.fromIterable(collectionOfNames, 2); + + String[] names = new String[] { "john", "doe", "anne" }; + Triplet triplet2 = Triplet.fromArray(names); + } + + @Test + public void whenGetValuexFromTuples_thenRetriveValueWithType() { + Quartet quartet = Quartet.with("john", 72.5, 32, "1051 SW"); + + String name = quartet.getValue0(); + Integer age = quartet.getValue2(); + assertThat(name).isEqualTo("john"); + assertThat(age).isEqualTo(32); + } + + @Test + public void whenGetKeyValue_thenGetKeyValue() { + KeyValue keyValue = KeyValue.with(5, "F"); + Integer key = keyValue.getKey(); + String value = keyValue.getValue(); + + assertThat(key).isEqualTo(5); + assertThat(value).isEqualTo("F"); + } + + @Test + public void whenGetLabelValue_thenGetLabelValue() { + LabelValue labelValue = LabelValue.with(5, "F"); + Integer key = labelValue.getLabel(); + String value = labelValue.getValue(); + + assertThat(key).isEqualTo(5); + assertThat(value).isEqualTo("F"); + } + + @Test + public void whenGetValueFromTuples_thenRetriveValueWithoutType() { + Quartet quartet = Quartet.with("john", 72.5, 32, "1051 SW"); + + String name = (String) quartet.getValue(0); + Integer age = (Integer) quartet.getValue(2); + assertThat(name).isEqualTo("john"); + assertThat(age).isEqualTo(32); + } + + @Test + public void whenSetValueInTuple_thenGetANewTuple() { + Pair john = Pair.with("john", 32); + Pair alex = john.setAt0("alex"); + assertThat(john.toString()).isNotEqualTo(alex.toString()); + } + + @Test + public void whenAddNewElement_thenCreateNewTuple() { + Pair pair1 = Pair.with("john", 32); + Triplet triplet1 = pair1.add("1051 SW"); + assertThat(triplet1.contains("john")); + assertThat(triplet1.contains(32)); + assertThat(triplet1.contains("1051 SW")); + + Pair pair2 = Pair.with("alex", 45); + Quartet quartet2 = pair1.add(pair2); + assertThat(quartet2.containsAll(pair1)); + assertThat(quartet2.containsAll(pair2)); + + Quartet quartet1 = pair1.add("alex", 45); + assertThat(quartet1.containsAll("alex", "john", 32, 45)); + + Triplet triplet2 = pair1.addAt1("1051 SW"); + assertThat(triplet2.indexOf("john")).isEqualTo(0); + assertThat(triplet2.indexOf("1051 SW")).isEqualTo(1); + assertThat(triplet2.indexOf(32)).isEqualTo(2); + + Unit unit = pair1.removeFrom0(); + assertThat(unit.contains(32)); + } + + @Test + public void whenCallingToList_thenReturnList() { + Quartet quartet = Quartet.with("john", 72.5, 32, "1051 SW"); + List list = quartet.toList(); + assertThat(list.size()).isEqualTo(4); + } + + @Test + public void whenCallingToArray_thenReturnArray() { + Quartet quartet = Quartet.with("john", 72.5, 32, "1051 SW"); + Object[] array = quartet.toArray(); + assertThat(array.length).isEqualTo(4); + } +} diff --git a/libraries/src/test/java/com/baeldung/javers/JaversTest.java b/libraries/src/test/java/com/baeldung/javers/JaversTest.java new file mode 100644 index 000000000000..e8e3e62e088b --- /dev/null +++ b/libraries/src/test/java/com/baeldung/javers/JaversTest.java @@ -0,0 +1,113 @@ +package com.baeldung.javers; + + +import org.javers.common.collections.Lists; +import org.javers.core.Javers; +import org.javers.core.JaversBuilder; +import org.javers.core.diff.Diff; +import org.javers.core.diff.changetype.NewObject; +import org.javers.core.diff.changetype.ObjectRemoved; +import org.javers.core.diff.changetype.ValueChange; +import org.javers.core.diff.changetype.container.ListChange; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class JaversTest { + + @Test + public void givenPersonObject_whenApplyModificationOnIt_thenShouldDetectChange() { + //given + Javers javers = JaversBuilder.javers().build(); + + Person person = new Person(1, "Michael Program"); + Person personAfterModification = new Person(1, "Michael Java"); + + //when + Diff diff = javers.compare(person, personAfterModification); + + //then + ValueChange change = diff.getChangesByType(ValueChange.class).get(0); + + assertThat(diff.getChanges()).hasSize(1); + assertThat(change.getPropertyName()).isEqualTo("name"); + assertThat(change.getLeft()).isEqualTo("Michael Program"); + assertThat(change.getRight()).isEqualTo("Michael Java"); + } + + + @Test + public void givenListOfPersons_whenCompare_ThenShouldDetectChanges() { + //given + Javers javers = JaversBuilder.javers().build(); + Person personThatWillBeRemoved = new Person(2, "Thomas Link"); + List oldList = Lists.asList(new Person(1, "Michael Program"), personThatWillBeRemoved); + List newList = Lists.asList(new Person(1, "Michael Not Program")); + + //when + Diff diff = javers.compareCollections(oldList, newList, Person.class); + + //then + assertThat(diff.getChanges()).hasSize(3); + + + ValueChange valueChange = diff.getChangesByType(ValueChange.class).get(0); + assertThat(valueChange.getPropertyName()).isEqualTo("name"); + assertThat(valueChange.getLeft()).isEqualTo("Michael Program"); + assertThat(valueChange.getRight()).isEqualTo("Michael Not Program"); + + ObjectRemoved objectRemoved = diff.getChangesByType(ObjectRemoved.class).get(0); + assertThat(objectRemoved.getAffectedObject().get().equals(personThatWillBeRemoved)).isTrue(); + + ListChange listChange = diff.getChangesByType(ListChange.class).get(0); + assertThat(listChange.getValueRemovedChanges().size()).isEqualTo(1); + + } + + @Test + public void givenListOfPerson_whenPersonHasNewAddress_thenDetectThatChange() { + //given + Javers javers = JaversBuilder.javers().build(); + + PersonWithAddress person = + new PersonWithAddress(1, "Tom", Arrays.asList(new Address("England"))); + + PersonWithAddress personWithNewAddress = + new PersonWithAddress(1, "Tom", + Arrays.asList(new Address("England"), new Address("USA"))); + + + //when + Diff diff = javers.compare(person, personWithNewAddress); + List objectsByChangeType = diff.getObjectsByChangeType(NewObject.class); + + //then + assertThat(objectsByChangeType).hasSize(1); + assertThat(objectsByChangeType.get(0).equals(new Address("USA"))); + } + + @Test + public void givenListOfPerson_whenPersonRemovedAddress_thenDetectThatChange() { + //given + Javers javers = JaversBuilder.javers().build(); + + PersonWithAddress person = + new PersonWithAddress(1, "Tom", Arrays.asList(new Address("England"))); + + PersonWithAddress personWithNewAddress = + new PersonWithAddress(1, "Tom", Collections.emptyList()); + + + //when + Diff diff = javers.compare(person, personWithNewAddress); + List objectsByChangeType = diff.getObjectsByChangeType(ObjectRemoved.class); + + //then + assertThat(objectsByChangeType).hasSize(1); + assertThat(objectsByChangeType.get(0).equals(new Address("England"))); + } +} diff --git a/libraries/src/test/java/com/baeldung/jetty/JettyTest.java b/libraries/src/test/java/com/baeldung/jetty/JettyTest.java new file mode 100644 index 000000000000..caf70f9af307 --- /dev/null +++ b/libraries/src/test/java/com/baeldung/jetty/JettyTest.java @@ -0,0 +1,58 @@ +package com.baeldung.jetty; + + +import org.apache.commons.io.IOUtils; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.HttpClientBuilder; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.nio.charset.StandardCharsets; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + +public class JettyTest { + private JettyServer jettyServer; + + @Before + public void setup() throws Exception { + jettyServer = new JettyServer(); + jettyServer.start(); + } + + @After + public void cleanup() throws Exception { + Thread.sleep(2000); + jettyServer.stop(); + } + + @Test + public void givenServer_whenSendRequestToBlockingServlet_thenReturnStatusOK() throws Exception { + //given + String url = "http://localhost:8090/status"; + HttpClient client = HttpClientBuilder.create().build(); + HttpGet request = new HttpGet(url); + HttpResponse response = client.execute(request); + + //then + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); + + } + + @Test + public void givenServer_whenSendRequestToNonBlockingServlet_thenReturnStatusOK() throws Exception { + //when + String url = "http://localhost:8090/heavy/async"; + HttpClient client = HttpClientBuilder.create().build(); + HttpGet request = new HttpGet(url); + HttpResponse response = client.execute(request); + + //then + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); + String responseContent = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); + assertThat(responseContent).isEqualTo("This is some heavy resource that will be served in an async way"); + } +} diff --git a/libraries/src/test/java/com/baeldung/jsonassert/JsonAssertTest.java b/libraries/src/test/java/com/baeldung/jsonassert/JsonAssertTest.java new file mode 100644 index 000000000000..b70703cc0874 --- /dev/null +++ b/libraries/src/test/java/com/baeldung/jsonassert/JsonAssertTest.java @@ -0,0 +1,129 @@ +package com.baeldung.jsonassert; + + +import static org.assertj.core.api.Assertions.assertThat; + +import org.json.JSONException; +import org.json.JSONObject; +import org.junit.Test; +import org.skyscreamer.jsonassert.Customization; +import org.skyscreamer.jsonassert.JSONAssert; +import org.skyscreamer.jsonassert.JSONCompareMode; +import org.skyscreamer.jsonassert.RegularExpressionValueMatcher; +import org.skyscreamer.jsonassert.comparator.ArraySizeComparator; +import org.skyscreamer.jsonassert.comparator.CustomComparator; + +public class JsonAssertTest { + + @Test + public void givenLenientode_whenAssertEqualsSameJsonString_thenPass() throws JSONException { + String actual = "{id:123,name:\"John\"}"; + JSONAssert.assertEquals("{id:123,name:\"John\"}", actual, JSONCompareMode.LENIENT); + + actual = "{id:123,name:\"John\",zip:\"33025\"}"; + JSONAssert.assertEquals("{id:123,name:\"John\"}", actual, JSONCompareMode.LENIENT); + } + + @Test + public void givenStrictMode_whenAssertNotEqualsExtendedJsonString_thenPass() throws JSONException { + String actual = "{id:123,name:\"John\"}"; + JSONAssert.assertNotEquals("{name:\"John\"}", actual, JSONCompareMode.STRICT); + } + + @Test + public void whenUsingCompareModeOrBoolean_thenBothAreSame() throws JSONException { + String actual = "{id:123,name:\"John\",zip:\"33025\"}"; + JSONAssert.assertEquals("{id:123,name:\"John\"}", actual, JSONCompareMode.LENIENT); + JSONAssert.assertEquals("{id:123,name:\"John\"}", actual, false); + + actual = "{id:123,name:\"John\"}"; + JSONAssert.assertNotEquals("{name:\"John\"}", actual, JSONCompareMode.STRICT); + JSONAssert.assertNotEquals("{name:\"John\"}", actual, true); + } + + @Test + public void givenDifferentOrderForJsonObject_whenAssertEquals_thenPass() throws JSONException { + String result = "{id:1,name:\"John\"}"; + + JSONAssert.assertEquals("{name:\"John\",id:1}", result, JSONCompareMode.STRICT); + JSONAssert.assertEquals("{name:\"John\",id:1}", result, JSONCompareMode.LENIENT); + } + + @Test + public void givenDifferentTypes_whenAssertEqualsSameValue_thenPass() throws JSONException { + JSONObject expected = new JSONObject(); + JSONObject actual = new JSONObject(); + expected.put("id", Integer.valueOf(12345)); + actual.put("id", Double.valueOf(12345)); + + JSONAssert.assertEquals(expected, actual, false); + JSONAssert.assertEquals(expected, actual, JSONCompareMode.LENIENT); + } + + @Test + public void givenNestedObjects_whenAssertEquals_thenPass() throws JSONException { + String result = "{id:1,name:\"Juergen\", address:{city:\"Hollywood\", " + + "state:\"LA\", zip:91601}}"; + JSONAssert.assertEquals("{id:1,name:\"Juergen\", address:{city:\"Hollywood\", " + + "state:\"LA\", zip:91601}}", result, false); + } + + @Test + public void whenMessageUsedInAssertion_thenDisplayMessageOnFailure() throws JSONException { + String actual = "{id:123,name:\"John\"}"; + String failureMessage = "Only one field is expected: name"; + try { + JSONAssert.assertEquals(failureMessage, "{name:\"John\"}", actual, JSONCompareMode.STRICT); + } catch (AssertionError ae) { + assertThat(ae.getMessage()).containsIgnoringCase(failureMessage); + } + } + + @Test + public void givenArray_whenComparing_thenOrderMustMatchForStrict() throws JSONException { + String result = "[Alex, Barbera, Charlie, Xavier]"; + JSONAssert.assertEquals("[Charlie, Alex, Xavier, Barbera]", result, JSONCompareMode.LENIENT); + JSONAssert.assertEquals("[Alex, Barbera, Charlie, Xavier]", result, JSONCompareMode.STRICT); + JSONAssert.assertNotEquals("[Charlie, Alex, Xavier, Barbera]", result, JSONCompareMode.STRICT); + } + + @Test + public void givenArray_whenComparingExtended_thenNotEqual() throws JSONException { + String result = "[1,2,3,4,5]"; + JSONAssert.assertEquals("[1,2,3,4,5]", result, JSONCompareMode.LENIENT); + JSONAssert.assertNotEquals("[1,2,3]", result, JSONCompareMode.LENIENT); + JSONAssert.assertNotEquals("[1,2,3,4,5,6]", result, JSONCompareMode.LENIENT); + } + + @Test + public void whenComparingSizeOfArray_thenPass() throws JSONException { + String names = "{names:[Alex, Barbera, Charlie, Xavier]}"; + JSONAssert.assertEquals( + "{names:[4]}", + names, + new ArraySizeComparator(JSONCompareMode.LENIENT)); + } + + @Test + public void whenComparingContentsOfArray_thenPass() throws JSONException { + String ratings = "{ratings:[3.2,3.5,4.1,5,1]}"; + JSONAssert.assertEquals( + "{ratings:[1,5]}", + ratings, + new ArraySizeComparator(JSONCompareMode.LENIENT)); + } + + @Test + public void givenValueMatcher_whenComparingUsingRegex_thenPass() throws IllegalArgumentException, JSONException { + JSONAssert.assertEquals("{entry:{id:x}}", "{entry:{id:1, id:2}}", + new CustomComparator( + JSONCompareMode.STRICT, + new Customization("entry.id", + new RegularExpressionValueMatcher("\\d")))); + + JSONAssert.assertNotEquals("{entry:{id:x}}", "{entry:{id:1, id:as}}", + new CustomComparator(JSONCompareMode.STRICT, + new Customization("entry.id", + new RegularExpressionValueMatcher("\\d")))); + } +} diff --git a/log-mdc/pom.xml b/log-mdc/pom.xml index 931e68a178f4..10dcb3eaf074 100644 --- a/log-mdc/pom.xml +++ b/log-mdc/pom.xml @@ -13,7 +13,7 @@ org.springframework spring-core - ${springframework.version} + ${springframework.version} org.springframework diff --git a/log4j2/README.md b/log4j2/README.md new file mode 100644 index 000000000000..3afd842c828a --- /dev/null +++ b/log4j2/README.md @@ -0,0 +1,3 @@ +### Relevant articles + +- [Intro to Log4j2 – Appenders, Layouts and Filters](http://www.baeldung.com/log4j2-appenders-layouts-filters) diff --git a/log4j2/pom.xml b/log4j2/pom.xml index a4c8f19f697a..c348982bcff7 100644 --- a/log4j2/pom.xml +++ b/log4j2/pom.xml @@ -43,7 +43,7 @@ org.apache.commons commons-dbcp2 - ${commons-dbcp2.version} + ${commons-dbcp2.version} diff --git a/log4j2/src/test/java/com/baeldung/logging/log4j2/tests/CustomLoggingTest.java b/log4j2/src/test/java/com/baeldung/logging/log4j2/tests/CustomLoggingTest.java index 1562b670683f..333b4f5e7878 100644 --- a/log4j2/src/test/java/com/baeldung/logging/log4j2/tests/CustomLoggingTest.java +++ b/log4j2/src/test/java/com/baeldung/logging/log4j2/tests/CustomLoggingTest.java @@ -31,78 +31,85 @@ public static void setup() throws Exception { } @Test - public void givenLoggerWithDefaultConfig_shouldLogToConsole() throws Exception { + public void givenLoggerWithDefaultConfig_whenLogToConsole_thanOK() throws Exception { Logger logger = LogManager.getLogger(getClass()); Exception e = new RuntimeException("This is only a test!"); + logger.info("This is a simple message at INFO level. " + "It will be hidden."); logger.error("This is a simple message at ERROR level. " + "This is the minimum visible level.", e); } @Test - public void givenLoggerWithConsoleConfig_shouldLogToConsoleInColors() throws Exception { + public void givenLoggerWithConsoleConfig_whenLogToConsoleInColors_thanOK() throws Exception { Logger logger = LogManager.getLogger("CONSOLE_PATTERN_APPENDER_MARKER"); + Exception e = new RuntimeException("This is only a test!"); + logger.trace("This is a colored message at TRACE level."); logger.debug("This is a colored message at DEBUG level. " + "This is the minimum visible level."); logger.info("This is a colored message at INFO level."); logger.warn("This is a colored message at WARN level."); - Exception e = new RuntimeException("This is only a test!"); logger.error("This is a colored message at ERROR level.", e); logger.fatal("This is a colored message at FATAL level."); } @Test - public void givenLoggerWithConsoleConfig_shouldFilterByMarker() throws Exception { + public void givenLoggerWithConsoleConfig_whenFilterByMarker_thanOK() throws Exception { Logger logger = LogManager.getLogger("CONSOLE_PATTERN_APPENDER_MARKER"); Marker appError = MarkerManager.getMarker("APP_ERROR"); - logger.error(appError, "This marker message at ERROR level should be hidden."); Marker connectionTrace = MarkerManager.getMarker("CONN_TRACE"); + + logger.error(appError, "This marker message at ERROR level should be hidden."); logger.trace(connectionTrace, "This is a marker message at TRACE level."); } @Test - public void givenLoggerWithConsoleConfig_shouldFilterByThreadContext() throws Exception { + public void givenLoggerWithConsoleConfig_whenFilterByThreadContext_thanOK() throws Exception { Logger logger = LogManager.getLogger("CONSOLE_PATTERN_APPENDER_THREAD_CONTEXT"); ThreadContext.put("userId", "1000"); logger.info("This is a log-visible user login. Maybe from an admin account?"); ThreadContext.put("userId", "1001"); logger.info("This is a log-invisible user login."); - } @Test - public void givenLoggerWithAsyncConfig_shouldLogToJsonFile() throws Exception { + public void givenLoggerWithAsyncConfig_whenLogToJsonFile_thanOK() throws Exception { Logger logger = LogManager.getLogger("ASYNC_JSON_FILE_APPENDER"); + final int count = 88; for (int i = 0; i < count; i++) { logger.info("This is async JSON message #{} at INFO level.", count); } + long logEventsCount = Files.lines(Paths.get("target/logfile.json")) .count(); assertTrue(logEventsCount > 0 && logEventsCount <= count); } @Test - public void givenLoggerWithFailoverConfig_shouldLog() throws Exception { + public void givenLoggerWithFailoverConfig_whenLog_thanOK() throws Exception { Logger logger = LogManager.getLogger("FAIL_OVER_SYSLOG_APPENDER"); + Exception e = new RuntimeException("This is only a test!"); + logger.trace("This is a syslog message at TRACE level."); logger.debug("This is a syslog message at DEBUG level."); logger.info("This is a syslog message at INFO level. This is the minimum visible level."); logger.warn("This is a syslog message at WARN level."); - Exception e = new RuntimeException("This is only a test!"); logger.error("This is a syslog message at ERROR level.", e); logger.fatal("This is a syslog message at FATAL level."); } @Test - public void givenLoggerWithJdbcConfig_shouldLogToDataSource() throws Exception { + public void givenLoggerWithJdbcConfig_whenLogToDataSource_thanOK() throws Exception { Logger logger = LogManager.getLogger("JDBC_APPENDER"); + final int count = 88; for (int i = 0; i < count; i++) { logger.info("This is JDBC message #{} at INFO level.", count); } Connection connection = ConnectionFactory.getConnection(); ResultSet resultSet = connection.createStatement() - .executeQuery("SELECT COUNT(*) AS ROW_COUNT FROM logs"); + .executeQuery("SELECT COUNT(*) AS ROW_COUNT FROM logs"); + int logCount = 0; if (resultSet.next()) { logCount = resultSet.getInt("ROW_COUNT"); @@ -111,8 +118,9 @@ public void givenLoggerWithJdbcConfig_shouldLogToDataSource() throws Exception { } @Test - public void givenLoggerWithRollingFileConfig_shouldLogToXMLFile() throws Exception { + public void givenLoggerWithRollingFileConfig_whenLogToXMLFile_thanOK() throws Exception { Logger logger = LogManager.getLogger("XML_ROLLING_FILE_APPENDER"); + final int count = 88; for (int i = 0; i < count; i++) { logger.info("This is rolling file XML message #{} at INFO level.", i); diff --git a/log4j2/src/test/resources/log4j2.xml b/log4j2/src/test/resources/log4j2.xml index 83c1184f1f72..21fd1da446af 100644 --- a/log4j2/src/test/resources/log4j2.xml +++ b/log4j2/src/test/resources/log4j2.xml @@ -19,12 +19,14 @@ - + @@ -53,9 +55,11 @@ + diff --git a/mapstruct/pom.xml b/mapstruct/pom.xml index 72a67d19db9d..81bcdb4cb99b 100644 --- a/mapstruct/pom.xml +++ b/mapstruct/pom.xml @@ -32,6 +32,12 @@ org.springframework spring-context ${springframework.version} + + + commons-logging + commons-logging + + org.springframework diff --git a/mesos-marathon/README.md b/mesos-marathon/README.md new file mode 100644 index 000000000000..3223eb2478e8 --- /dev/null +++ b/mesos-marathon/README.md @@ -0,0 +1,3 @@ +### Relevant articles + +- [Simple Jenkins Pipeline with Marathon and Mesos](http://www.baeldung.com/jenkins-pipeline-with-marathon-mesos) diff --git a/mesos-marathon/pom.xml b/mesos-marathon/pom.xml index ca17a5c4c477..92b56e721bf9 100644 --- a/mesos-marathon/pom.xml +++ b/mesos-marathon/pom.xml @@ -40,6 +40,16 @@ + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*IntegrationTest.java + **/*LiveTest.java + + + diff --git a/mesos-marathon/src/main/java/com/mogronalol/DemoApplication.java b/mesos-marathon/src/main/java/com/baeldung/DemoApplication.java similarity index 81% rename from mesos-marathon/src/main/java/com/mogronalol/DemoApplication.java rename to mesos-marathon/src/main/java/com/baeldung/DemoApplication.java index f757178026e9..b2c530227728 100644 --- a/mesos-marathon/src/main/java/com/mogronalol/DemoApplication.java +++ b/mesos-marathon/src/main/java/com/baeldung/DemoApplication.java @@ -1,10 +1,8 @@ -package com.mogronalol; +package com.baeldung; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import javax.annotation.PostConstruct; - @SpringBootApplication public class DemoApplication { diff --git a/mesos-marathon/src/main/java/com/mogronalol/HelloController.java b/mesos-marathon/src/main/java/com/baeldung/HelloController.java similarity index 82% rename from mesos-marathon/src/main/java/com/mogronalol/HelloController.java rename to mesos-marathon/src/main/java/com/baeldung/HelloController.java index 2059280ba0c2..83eca1f501c7 100644 --- a/mesos-marathon/src/main/java/com/mogronalol/HelloController.java +++ b/mesos-marathon/src/main/java/com/baeldung/HelloController.java @@ -1,6 +1,5 @@ -package com.mogronalol; +package com.baeldung; -import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; diff --git a/mesos-marathon/src/test/java/com/mogronalol/DemoApplicationTests.java b/mesos-marathon/src/test/java/com/baeldung/DemoApplicationIntegrationTest.java similarity index 88% rename from mesos-marathon/src/test/java/com/mogronalol/DemoApplicationTests.java rename to mesos-marathon/src/test/java/com/baeldung/DemoApplicationIntegrationTest.java index 5e88f9a70fb6..85331516f9ec 100644 --- a/mesos-marathon/src/test/java/com/mogronalol/DemoApplicationTests.java +++ b/mesos-marathon/src/test/java/com/baeldung/DemoApplicationIntegrationTest.java @@ -1,11 +1,10 @@ -package com.mogronalol; +package com.baeldung; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.context.embedded.LocalServerPort; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.web.client.RestTemplate; @@ -13,7 +12,7 @@ @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = {DemoApplication.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -public class DemoApplicationTests { +public class DemoApplicationIntegrationTest { private RestTemplate restTemplate; diff --git a/metrics/pom.xml b/metrics/pom.xml index 0f5cb2f135f8..6111ebf30b90 100644 --- a/metrics/pom.xml +++ b/metrics/pom.xml @@ -69,6 +69,18 @@ ${maven.compiler.target} + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*IntegrationTest.java + **/*LongRunningUnitTest.java + **/*ManualTest.java + + true + + diff --git a/metrics/src/test/java/com/baeldung/metrics/core/MetricsTest.java b/metrics/src/test/java/com/baeldung/metrics/core/MetricsIntegrationTest.java similarity index 99% rename from metrics/src/test/java/com/baeldung/metrics/core/MetricsTest.java rename to metrics/src/test/java/com/baeldung/metrics/core/MetricsIntegrationTest.java index e876de6e65b9..352911f1e1ce 100644 --- a/metrics/src/test/java/com/baeldung/metrics/core/MetricsTest.java +++ b/metrics/src/test/java/com/baeldung/metrics/core/MetricsIntegrationTest.java @@ -11,7 +11,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; -public class MetricsTest { +public class MetricsIntegrationTest { @Test public void whenMarkMeter_thenCorrectRates() throws InterruptedException { Meter meter = new Meter(); diff --git a/mockito/src/main/resources/logback.xml b/mockito/src/main/resources/logback.xml index 1146dade632e..ec0dc2469ae0 100644 --- a/mockito/src/main/resources/logback.xml +++ b/mockito/src/main/resources/logback.xml @@ -1,20 +1,19 @@ + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - + + - - - - - - - - - + + + + + \ No newline at end of file diff --git a/mockito/src/test/java/org/baeldung/mockito/MockitoConfigExamplesTest.java b/mockito/src/test/java/org/baeldung/mockito/MockitoConfigExamplesTest.java index de6e7fca72d7..5050a2d6bc64 100644 --- a/mockito/src/test/java/org/baeldung/mockito/MockitoConfigExamplesTest.java +++ b/mockito/src/test/java/org/baeldung/mockito/MockitoConfigExamplesTest.java @@ -2,8 +2,6 @@ import org.junit.Test; import org.mockito.Mockito; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic; import static org.hamcrest.Matchers.equalTo; @@ -69,7 +67,7 @@ public final void givenBehaviorIsConfiguredToThrowExceptionOnSecondCall_whenCall } @Test - public final void whenMockMethodCallIsConfiguredToCallTheRealMethod_thenRealMetehodIsCalled() { + public final void whenMockMethodCallIsConfiguredToCallTheRealMethod_thenRealMethodIsCalled() { final MyList listMock = Mockito.mock(MyList.class); when(listMock.size()).thenCallRealMethod(); @@ -77,14 +75,9 @@ public final void whenMockMethodCallIsConfiguredToCallTheRealMethod_thenRealMete } @Test - public final void whenMockMethodCallIsConfiguredWithCustomAnswer_thenRealMetehodIsCalled() { + public final void whenMockMethodCallIsConfiguredWithCustomAnswer_thenRealMethodIsCalled() { final MyList listMock = Mockito.mock(MyList.class); - doAnswer(new Answer() { - @Override - public final String answer(final InvocationOnMock invocation) { - return "Always the same"; - } - }).when(listMock).get(anyInt()); + doAnswer(invocation -> "Always the same").when(listMock).get(anyInt()); final String element = listMock.get(1); assertThat(element, is(equalTo("Always the same"))); diff --git a/spring-mvc-no-xml/src/test/resources/.gitignore b/mockito2/.gitignore similarity index 80% rename from spring-mvc-no-xml/src/test/resources/.gitignore rename to mockito2/.gitignore index 83c05e60c802..7f300600e6ce 100644 --- a/spring-mvc-no-xml/src/test/resources/.gitignore +++ b/mockito2/.gitignore @@ -1,13 +1,14 @@ *.class +.settings +.project + #folders# /target -/neoDb* -/data /src/main/webapp/WEB-INF/classes */META-INF/* # Packaged files # *.jar *.war -*.ear \ No newline at end of file +*.ear diff --git a/mockito2/README.md b/mockito2/README.md new file mode 100644 index 000000000000..49741c66d16e --- /dev/null +++ b/mockito2/README.md @@ -0,0 +1,7 @@ +### Relevant articles + +- [Mockito’s Java 8 Features](http://www.baeldung.com/mockito-2-java-8) + +## Mockito 2 and Java 8 Tips + +Examples on how to leverage Java 8 new features with Mockito version 2 diff --git a/solr-fulltext-search/pom.xml b/mockito2/pom.xml similarity index 71% rename from solr-fulltext-search/pom.xml rename to mockito2/pom.xml index 4afcb5838ad8..e116647009a6 100644 --- a/solr-fulltext-search/pom.xml +++ b/mockito2/pom.xml @@ -3,41 +3,37 @@ 4.0.0 com.baeldung - solr-fulltext-search + mockito2 0.0.1-SNAPSHOT jar - solr-fulltext-search - - - - UTF-8 - 2.19.1 - + mockito-2-with-java8 - - org.apache.solr - solr-solrj - 6.1.0 - - - log4j - log4j - 1.2.16 - - - junit - junit - 4.12 - test - + + + junit + junit + ${junit.version} + test + + + org.mockito + mockito-core + ${mockito.version} + test + maven-compiler-plugin + ${maven-compiler-plugin.version} + + 1.8 + 1.8 + org.apache.maven.plugins @@ -87,4 +83,16 @@ - \ No newline at end of file + + + UTF-8 + + + 4.12 + 2.7.5 + + + 3.6.0 + 2.19.1 + + diff --git a/mockito2/src/main/java/com/baeldung/mockito/java8/JobPosition.java b/mockito2/src/main/java/com/baeldung/mockito/java8/JobPosition.java new file mode 100644 index 000000000000..c7712fa47e65 --- /dev/null +++ b/mockito2/src/main/java/com/baeldung/mockito/java8/JobPosition.java @@ -0,0 +1,20 @@ +package com.baeldung.mockito.java8; + +public class JobPosition { + private String title; + + public JobPosition() {} + + public JobPosition(String title) { + super(); + this.title = title; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } +} diff --git a/mockito2/src/main/java/com/baeldung/mockito/java8/JobService.java b/mockito2/src/main/java/com/baeldung/mockito/java8/JobService.java new file mode 100644 index 000000000000..85a59b18a4c6 --- /dev/null +++ b/mockito2/src/main/java/com/baeldung/mockito/java8/JobService.java @@ -0,0 +1,20 @@ +package com.baeldung.mockito.java8; + +import java.util.Optional; +import java.util.stream.Stream; + +public interface JobService { + Optional findCurrentJobPosition(Person person); + + default boolean assignJobPosition(Person person, JobPosition jobPosition) { + if (!findCurrentJobPosition(person).isPresent()) { + person.setCurrentJobPosition(jobPosition); + + return true; + } else { + return false; + } + } + + Stream listJobs(Person person); +} diff --git a/mockito2/src/main/java/com/baeldung/mockito/java8/Person.java b/mockito2/src/main/java/com/baeldung/mockito/java8/Person.java new file mode 100644 index 000000000000..4f7af49f9daa --- /dev/null +++ b/mockito2/src/main/java/com/baeldung/mockito/java8/Person.java @@ -0,0 +1,28 @@ +package com.baeldung.mockito.java8; + +public class Person { + private String name; + private JobPosition currentJobPosition; + + public Person() {} + + public Person(String name) { + this.name = name; + } + + public JobPosition getCurrentJobPosition() { + return currentJobPosition; + } + + public void setCurrentJobPosition(JobPosition currentJobPosition) { + this.currentJobPosition = currentJobPosition; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/mockito2/src/main/java/com/baeldung/mockito/java8/UnemploymentService.java b/mockito2/src/main/java/com/baeldung/mockito/java8/UnemploymentService.java new file mode 100644 index 000000000000..5859ab31b02c --- /dev/null +++ b/mockito2/src/main/java/com/baeldung/mockito/java8/UnemploymentService.java @@ -0,0 +1,11 @@ +package com.baeldung.mockito.java8; + +import java.util.Optional; + +public interface UnemploymentService { + + boolean personIsEntitledToUnemploymentSupport(Person person); + + Optional searchJob(Person person, String searchString); + +} diff --git a/mockito2/src/main/java/com/baeldung/mockito/java8/UnemploymentServiceImpl.java b/mockito2/src/main/java/com/baeldung/mockito/java8/UnemploymentServiceImpl.java new file mode 100644 index 000000000000..6f189b46eada --- /dev/null +++ b/mockito2/src/main/java/com/baeldung/mockito/java8/UnemploymentServiceImpl.java @@ -0,0 +1,26 @@ +package com.baeldung.mockito.java8; + +import java.util.Optional; +import java.util.stream.Stream; + +public class UnemploymentServiceImpl implements UnemploymentService { + private final JobService jobService; + + public UnemploymentServiceImpl(JobService jobService) { + this.jobService = jobService; + } + + @Override + public boolean personIsEntitledToUnemploymentSupport(Person person) { + Optional optional = jobService.findCurrentJobPosition(person); + + return !optional.isPresent(); + } + + @Override + public Optional searchJob(Person person, String searchString) { + Stream stream = jobService.listJobs(person); + + return stream.filter((j) -> j.getTitle().contains(searchString)).findFirst(); + } +} diff --git a/mockito2/src/test/java/com/baeldung/mockito/java8/ArgumentMatcherWithLambdaUnitTest.java b/mockito2/src/test/java/com/baeldung/mockito/java8/ArgumentMatcherWithLambdaUnitTest.java new file mode 100644 index 000000000000..2efac513b726 --- /dev/null +++ b/mockito2/src/test/java/com/baeldung/mockito/java8/ArgumentMatcherWithLambdaUnitTest.java @@ -0,0 +1,44 @@ +package com.baeldung.mockito.java8; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; + +import java.util.Optional; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentMatchers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + + +public class ArgumentMatcherWithLambdaUnitTest { + + @InjectMocks + private UnemploymentServiceImpl unemploymentService; + + @Mock + private JobService jobService; + + @Test + public void whenPersonWithJob_thenIsNotEntitled() { + Person peter = new Person("Peter"); + Person linda = new Person("Linda"); + + JobPosition teacher = new JobPosition("Teacher"); + + when(jobService.findCurrentJobPosition( + ArgumentMatchers.argThat((p) -> p.getName().equals("Peter"))) + ).thenReturn(Optional.of(teacher)); + + assertTrue(unemploymentService.personIsEntitledToUnemploymentSupport(linda)); + assertFalse(unemploymentService.personIsEntitledToUnemploymentSupport(peter)); + } + + @Before + public void init() { + MockitoAnnotations.initMocks(this); + } +} diff --git a/mockito2/src/test/java/com/baeldung/mockito/java8/ArgumentMatcherWithoutLambdaUnitTest.java b/mockito2/src/test/java/com/baeldung/mockito/java8/ArgumentMatcherWithoutLambdaUnitTest.java new file mode 100644 index 000000000000..aaa8d0358521 --- /dev/null +++ b/mockito2/src/test/java/com/baeldung/mockito/java8/ArgumentMatcherWithoutLambdaUnitTest.java @@ -0,0 +1,51 @@ +package com.baeldung.mockito.java8; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.*; + +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; + + +public class ArgumentMatcherWithoutLambdaUnitTest { + + private class PeterArgumentMatcher implements ArgumentMatcher { + + @Override + public boolean matches(Person p) { + return p + .getName() + .equals("Peter"); + } + } + + @InjectMocks + private UnemploymentServiceImpl unemploymentService; + + @Mock + private JobService jobService; + + @Test + public void whenPersonWithJob_thenIsNotEntitled() { + Person peter = new Person("Peter"); + Person linda = new Person("Linda"); + + JobPosition teacher = new JobPosition("Teacher"); + + when(jobService.findCurrentJobPosition( + ArgumentMatchers.argThat(new PeterArgumentMatcher())) + ).thenReturn(Optional.of(teacher)); + + assertTrue(unemploymentService.personIsEntitledToUnemploymentSupport(linda)); + assertFalse(unemploymentService.personIsEntitledToUnemploymentSupport(peter)); + } + + @Before + public void init() { + MockitoAnnotations.initMocks(this); + } +} diff --git a/mockito2/src/test/java/com/baeldung/mockito/java8/CustomAnswerWithLambdaUnitTest.java b/mockito2/src/test/java/com/baeldung/mockito/java8/CustomAnswerWithLambdaUnitTest.java new file mode 100644 index 000000000000..06e9bca6d392 --- /dev/null +++ b/mockito2/src/test/java/com/baeldung/mockito/java8/CustomAnswerWithLambdaUnitTest.java @@ -0,0 +1,46 @@ +package com.baeldung.mockito.java8; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.stream.Stream; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +public class CustomAnswerWithLambdaUnitTest { + + @InjectMocks + private UnemploymentServiceImpl unemploymentService; + + @Mock + private JobService jobService; + + @Test + public void whenPersonWithJobHistory_thenSearchReturnsValue() { + Person peter = new Person("Peter"); + + assertEquals("Teacher", unemploymentService.searchJob(peter, "").get().getTitle()); + } + + @Test + public void whenPersonWithNoJobHistory_thenSearchReturnsEmpty() { + Person linda = new Person("Linda"); + + assertFalse(unemploymentService.searchJob(linda, "").isPresent()); + } + + @Before + public void init() { + MockitoAnnotations.initMocks(this); + + when(jobService.listJobs(any(Person.class))).then((i) -> + Stream.of(new JobPosition("Teacher")) + .filter(p -> ((Person) i.getArgument(0)).getName().equals("Peter"))); + } +} diff --git a/mockito2/src/test/java/com/baeldung/mockito/java8/CustomAnswerWithoutLambdaUnitTest.java b/mockito2/src/test/java/com/baeldung/mockito/java8/CustomAnswerWithoutLambdaUnitTest.java new file mode 100644 index 000000000000..d5b9d6d1cedb --- /dev/null +++ b/mockito2/src/test/java/com/baeldung/mockito/java8/CustomAnswerWithoutLambdaUnitTest.java @@ -0,0 +1,61 @@ +package com.baeldung.mockito.java8; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.util.stream.Stream; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + + +public class CustomAnswerWithoutLambdaUnitTest { + + private class PersonAnswer implements Answer> { + + @Override + public Stream answer(InvocationOnMock invocation) throws Throwable { + Person person = invocation.getArgument(0); + + if(person.getName().equals("Peter")) { + return Stream.builder().add(new JobPosition("Teacher")).build(); + } + + return Stream.empty(); + } + } + + @InjectMocks + private UnemploymentServiceImpl unemploymentService; + + @Mock + private JobService jobService; + + @Test + public void whenPersonWithJobHistory_thenSearchReturnsValue() { + Person peter = new Person("Peter"); + + assertEquals("Teacher", unemploymentService.searchJob(peter, "").get().getTitle()); + } + + @Test + public void whenPersonWithNoJobHistory_thenSearchReturnsEmpty() { + Person linda = new Person("Linda"); + + assertFalse(unemploymentService.searchJob(linda, "").isPresent()); + } + + @Before + public void init() { + MockitoAnnotations.initMocks(this); + + when(jobService.listJobs(any(Person.class))).then(new PersonAnswer()); + } +} diff --git a/mockito2/src/test/java/com/baeldung/mockito/java8/JobServiceUnitTest.java b/mockito2/src/test/java/com/baeldung/mockito/java8/JobServiceUnitTest.java new file mode 100644 index 000000000000..9ea5c1db4711 --- /dev/null +++ b/mockito2/src/test/java/com/baeldung/mockito/java8/JobServiceUnitTest.java @@ -0,0 +1,44 @@ +package com.baeldung.mockito.java8; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.doCallRealMethod; +import static org.mockito.Mockito.when; + +import java.util.Optional; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +public class JobServiceUnitTest { + @Mock + private JobService jobService; + + @Test + public void givenDefaultMethod_whenCallRealMethod_thenNoExceptionIsRaised() { + Person person = new Person(); + + when(jobService.findCurrentJobPosition(person)).thenReturn(Optional.of(new JobPosition())); + doCallRealMethod().when(jobService).assignJobPosition(Mockito.any(Person.class), Mockito.any(JobPosition.class)); + + assertFalse(jobService.assignJobPosition(person, new JobPosition())); + } + + @Test + public void givenReturnIsOfTypeOptional_whenDefaultValueIsReturned_thenValueIsEmpty() { + Person person = new Person(); + + when(jobService.findCurrentJobPosition(person)).thenReturn(Optional.empty()); + doCallRealMethod().when(jobService).assignJobPosition(Mockito.any(Person.class), Mockito.any(JobPosition.class)); + + assertTrue(jobService.assignJobPosition(person, new JobPosition())); + } + + @Before + public void init() { + MockitoAnnotations.initMocks(this); + } +} diff --git a/mockito2/src/test/java/com/baeldung/mockito/java8/UnemploymentServiceImplUnitTest.java b/mockito2/src/test/java/com/baeldung/mockito/java8/UnemploymentServiceImplUnitTest.java new file mode 100644 index 000000000000..b3b71e5bf9eb --- /dev/null +++ b/mockito2/src/test/java/com/baeldung/mockito/java8/UnemploymentServiceImplUnitTest.java @@ -0,0 +1,62 @@ +package com.baeldung.mockito.java8; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.when; + +import java.util.Optional; +import java.util.stream.Stream; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +public class UnemploymentServiceImplUnitTest { + @Mock + private JobService jobService; + + @InjectMocks + private UnemploymentServiceImpl unemploymentService; + + @Test + public void givenReturnIsOfTypeOptional_whenMocked_thenValueIsEmpty() { + Person person = new Person(); + + when(jobService.findCurrentJobPosition(any(Person.class))).thenReturn(Optional.empty()); + + assertTrue(unemploymentService.personIsEntitledToUnemploymentSupport(person)); + } + + @Test + public void givenReturnIsOfTypeOptional_whenDefaultValueIsReturned_thenValueIsEmpty() { + Person person = new Person(); + + // This will fail when Mockito 1 is used + assertTrue(unemploymentService.personIsEntitledToUnemploymentSupport(person)); + } + + @Test + public void givenReturnIsOfTypeStream_whenMocked_thenValueIsEmpty() { + Person person = new Person(); + + when(jobService.listJobs(any(Person.class))).thenReturn(Stream.empty()); + + assertFalse(unemploymentService.searchJob(person, "").isPresent()); + } + + @Test + public void givenReturnIsOfTypeStream_whenDefaultValueIsReturned_thenValueIsEmpty() { + Person person = new Person(); + + // This will fail when Mockito 1 is used + assertFalse(unemploymentService.searchJob(person, "").isPresent()); + } + + @Before + public void init() { + MockitoAnnotations.initMocks(this); + } +} diff --git a/spring-security-basic-auth/.gitignore b/mybatis/.gitignore similarity index 71% rename from spring-security-basic-auth/.gitignore rename to mybatis/.gitignore index 83c05e60c802..a5ecefc44c31 100644 --- a/spring-security-basic-auth/.gitignore +++ b/mybatis/.gitignore @@ -1,13 +1,16 @@ *.class +.settings +.project +*.log + #folders# /target -/neoDb* -/data /src/main/webapp/WEB-INF/classes */META-INF/* +/testdb1 # Packaged files # *.jar *.war -*.ear \ No newline at end of file +*.ear diff --git a/mybatis/pom.xml b/mybatis/pom.xml new file mode 100644 index 000000000000..56de727b2130 --- /dev/null +++ b/mybatis/pom.xml @@ -0,0 +1,34 @@ + + + 4.0.0 + + com.baeldung + mybatis + 1.0.0-SNAPSHOT + + 4.12 + 10.13.1.1 + 3.2.2 + + + + org.apache.derby + derby + ${derby.version} + + + org.mybatis + mybatis + ${mybatis.version} + + + junit + junit + ${junit.version} + test + + + + \ No newline at end of file diff --git a/mybatis/src/main/java/com/baeldung/mybatis/mapper/AddressMapper.java b/mybatis/src/main/java/com/baeldung/mybatis/mapper/AddressMapper.java new file mode 100644 index 000000000000..e96a4837db57 --- /dev/null +++ b/mybatis/src/main/java/com/baeldung/mybatis/mapper/AddressMapper.java @@ -0,0 +1,21 @@ +package com.baeldung.mybatis.mapper; + +import com.baeldung.mybatis.model.Address; +import com.baeldung.mybatis.model.Person; +import org.apache.ibatis.annotations.*; + +public interface AddressMapper { + + @Insert("Insert into address (streetAddress,personId) values(#{streetAddress},#{personId})") + @Options(useGeneratedKeys = true, flushCache = true) + public Integer saveAddress(Address address); + + @Select("SELECT addressId, streetAddress FROM Address WHERE addressId = #{addressId}") + @Results(value = { @Result(property = "addressId", column = "addressId"), + @Result(property = "streetAddress", column = "streetAddress"), + @Result(property = "person", column = "personId", javaType = Person.class, one = @One(select = "getPerson")) }) + Address getAddresses(Integer addressID); + + @Select("SELECT personId FROM address WHERE addressId = #{addressId})") + Person getPerson(Integer personId); +} diff --git a/mybatis/src/main/java/com/baeldung/mybatis/mapper/PersonMapper.java b/mybatis/src/main/java/com/baeldung/mybatis/mapper/PersonMapper.java new file mode 100644 index 000000000000..7021777ed186 --- /dev/null +++ b/mybatis/src/main/java/com/baeldung/mybatis/mapper/PersonMapper.java @@ -0,0 +1,47 @@ +package com.baeldung.mybatis.mapper; + +import com.baeldung.mybatis.model.Address; +import com.baeldung.mybatis.model.Person; +import com.baeldung.mybatis.utils.MyBatisUtil; +import org.apache.ibatis.annotations.*; +import org.apache.ibatis.mapping.StatementType; + +import java.util.List; +import java.util.Map; + +public interface PersonMapper { + + @Insert("Insert into person(name) values (#{name})") + public Integer save(Person person); + + @Update("Update Person set name= #{name} where personId=#{personId}") + public void updatePerson(Person person); + + @Delete("Delete from Person where personId=#{personId}") + public void deletePersonById(Integer personId); + + @Select("SELECT person.personId, person.name FROM person WHERE person.personId = #{personId}") + Person getPerson(Integer personId); + + @Select("Select personId,name from Person where personId=#{personId}") + @Results(value = { @Result(property = "personId", column = "personId"), @Result(property = "name", column = "name"), + @Result(property = "addresses", javaType = List.class, column = "personId", many = @Many(select = "getAddresses")) + + }) + public Person getPersonById(Integer personId); + + @Select("select addressId,streetAddress,personId from address where personId=#{personId}") + public Address getAddresses(Integer personId); + + @Select("select * from Person ") + @MapKey("personId") + Map getAllPerson(); + + @SelectProvider(type = MyBatisUtil.class, method = "getPersonByName") + public Person getPersonByName(String name); + + @Select(value = "{ CALL getPersonByProc( #{personId, mode=IN, jdbcType=INTEGER})}") + @Options(statementType = StatementType.CALLABLE) + public Person getPersonByProc(Integer personId); + +} diff --git a/mybatis/src/main/java/com/baeldung/mybatis/model/Address.java b/mybatis/src/main/java/com/baeldung/mybatis/model/Address.java new file mode 100644 index 000000000000..ea9a91c66651 --- /dev/null +++ b/mybatis/src/main/java/com/baeldung/mybatis/model/Address.java @@ -0,0 +1,46 @@ +package com.baeldung.mybatis.model; + +public class Address { + + private Integer addressId; + private String streetAddress; + private Integer personId; + + public Address() { + } + + public Integer getPersonId() { + return personId; + } + + public void setPersonId(Integer personId) { + this.personId = personId; + } + + public Address(String streetAddress) { + this.streetAddress = streetAddress; + } + + public Person getPerson() { + return person; + } + + public void setPerson(Person person) { + this.person = person; + } + + private Person person; + + public Address(int i, String name) { + this.streetAddress = name; + } + + public Integer getAddressId() { + return addressId; + } + + public String getStreetAddress() { + return streetAddress; + } + +} diff --git a/mybatis/src/main/java/com/baeldung/mybatis/model/Person.java b/mybatis/src/main/java/com/baeldung/mybatis/model/Person.java new file mode 100644 index 000000000000..2de08a9f7c11 --- /dev/null +++ b/mybatis/src/main/java/com/baeldung/mybatis/model/Person.java @@ -0,0 +1,40 @@ +package com.baeldung.mybatis.model; + +import java.util.ArrayList; +import java.util.List; + +public class Person { + + private Integer personId; + private String name; + private List
addresses; + + public Person() { + } + + public Person(Integer personId, String name) { + this.personId = personId; + this.name = name; + addresses = new ArrayList
(); + } + + public Person(String name) { + this.name = name; + } + + public Integer getPersonId() { + return personId; + } + + public String getName() { + return name; + } + + public void addAddress(Address address) { + addresses.add(address); + } + + public List
getAddresses() { + return addresses; + } +} diff --git a/mybatis/src/main/java/com/baeldung/mybatis/utils/MyBatisUtil.java b/mybatis/src/main/java/com/baeldung/mybatis/utils/MyBatisUtil.java new file mode 100644 index 000000000000..da323a5c24cb --- /dev/null +++ b/mybatis/src/main/java/com/baeldung/mybatis/utils/MyBatisUtil.java @@ -0,0 +1,47 @@ +package com.baeldung.mybatis.utils; + +import com.baeldung.mybatis.mapper.AddressMapper; +import com.baeldung.mybatis.mapper.PersonMapper; +import org.apache.ibatis.datasource.pooled.PooledDataSource; +import org.apache.ibatis.jdbc.SQL; +import org.apache.ibatis.mapping.Environment; +import org.apache.ibatis.session.Configuration; +import org.apache.ibatis.session.SqlSessionFactory; +import org.apache.ibatis.session.SqlSessionFactoryBuilder; +import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; + +import javax.sql.DataSource; + +public class MyBatisUtil { + public static final String DRIVER = "org.apache.derby.jdbc.EmbeddedDriver"; + public static final String URL = "jdbc:derby:testdb1;create=true"; + public static final String USERNAME = "sa"; + public static final String PASSWORD = "pass123"; + private static SqlSessionFactory sqlSessionFactory; + + public static SqlSessionFactory buildqlSessionFactory() { + DataSource dataSource = new PooledDataSource(DRIVER, URL, USERNAME, PASSWORD); + Environment environment = new Environment("Development", new JdbcTransactionFactory(), dataSource); + Configuration configuration = new Configuration(environment); + configuration.addMapper(PersonMapper.class); + configuration.addMapper(AddressMapper.class); + SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); + SqlSessionFactory factory = builder.build(configuration); + return factory; + + } + + public static SqlSessionFactory getSqlSessionFactory() { + return sqlSessionFactory; + } + + public String getPersonByName(String name) { + return new SQL() { + { + SELECT("*"); + FROM("person"); + WHERE("name like #{name} || '%'"); + } + }.toString(); + } +} diff --git a/pdf/pom.xml b/pdf/pom.xml index 332e1f52449e..53232377b519 100644 --- a/pdf/pom.xml +++ b/pdf/pom.xml @@ -37,11 +37,23 @@ org.apache.pdfbox pdfbox-tools ${pdfbox-tools.version} + + + commons-logging + commons-logging + + net.sf.cssbox pdf2dom ${pdf2dom.version} + + + commons-logging + commons-logging + + com.itextpdf diff --git a/pom.xml b/pom.xml index 014e4016c5ce..7671d05d7531 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ UTF-8 refs/heads/master - false + @@ -25,6 +25,7 @@ aspectj assertj autovalue + axon cdi @@ -34,7 +35,7 @@ deltaspike dozer - ejb + feign flyway @@ -42,14 +43,17 @@ + groovy-spock gson guava guava18 guava19 + guice disruptor handling-spring-static-resources hazelcast + hbase httpclient hystrix @@ -57,12 +61,13 @@ immutables jackson - java-cassandra + javaslang javax-servlets javaxval jaxb - jee7 + + jjwt jooq jpa-storedprocedure @@ -71,6 +76,7 @@ json jsoup junit5 + jws kotlin @@ -84,12 +90,14 @@ metrics mesos-marathon mockito + mockito2 mocks orika patterns pdf + protobuffer querydsl @@ -102,24 +110,26 @@ rxjava selenium-junit-testng - solr-fulltext-search + solr spark-java + spring-akka spring-amqp spring-all + spring-amqp-simple spring-apache-camel - spring-autowire spring-batch spring-boot - spring-boot-servlet spring-cloud-data-flow spring-cloud spring-core spring-cucumber + spring-aop spring-data-cassandra spring-data-couchbase-2 spring-data-dynamodb spring-data-elasticsearch + spring-data-javaslang spring-data-mongodb spring-data-neo4j spring-data-redis @@ -130,17 +140,19 @@ spring-freemarker spring-hibernate3 spring-hibernate4 + spring-hibernate5 spring-integration spring-jersey spring-jms spring-jooq spring-jpa + spring-kafka spring-katharsis + spring-ldap spring-mockito spring-mvc-email spring-mvc-forms spring-mvc-java - spring-mvc-no-xml spring-mvc-tiles spring-mvc-velocity spring-mvc-web-vs-initializer @@ -152,7 +164,6 @@ spring-rest-angular spring-rest-docs spring-rest - spring-security-basic-auth spring-security-cache-control spring-security-client/spring-security-jsp-authentication spring-security-client/spring-security-jsp-authorize @@ -179,6 +190,7 @@ spring-sleuth spring-social-login spring-spel + spring-state-machine spring-thymeleaf spring-userservice spring-zuul @@ -200,11 +212,39 @@ apache-solrj rabbitmq - - + vertx + spring-data-gemfire + cucumber + mybatis + + + org.codehaus.mojo + exec-maven-plugin + 1.6.0 + + maven + hibernate5 + + + + + org.apache.maven.plugins + maven-surefire-plugin + + 3 + true + + **/*IntegrationTest.java + **/*LongRunningUnitTest.java + **/*ManualTest.java + + true + + + - + \ No newline at end of file diff --git a/protobuffer/README.md b/protobuffer/README.md new file mode 100644 index 000000000000..4dcdb3cf5254 --- /dev/null +++ b/protobuffer/README.md @@ -0,0 +1,3 @@ +### Relevant articles + +- [Introduction to Google Protocol Buffer](http://www.baeldung.com/google-protocol-buffer) diff --git a/protobuffer/pom.xml b/protobuffer/pom.xml new file mode 100644 index 000000000000..17394716e2e1 --- /dev/null +++ b/protobuffer/pom.xml @@ -0,0 +1,48 @@ + + + + parent-modules + com.baeldung + 1.0.0-SNAPSHOT + + 4.0.0 + + protobuffer + + + + com.google.protobuf + protobuf-java + ${protobuf.version} + + + junit + junit + ${junit.version} + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + 1.8 + 1.8 + + + + + + + 3.2.0 + 4.12 + 3.6.0 + + + \ No newline at end of file diff --git a/protobuffer/src/main/java/com/baeldung/protobuf/AddressBookProtos.java b/protobuffer/src/main/java/com/baeldung/protobuf/AddressBookProtos.java new file mode 100644 index 000000000000..ba885830b63c --- /dev/null +++ b/protobuffer/src/main/java/com/baeldung/protobuf/AddressBookProtos.java @@ -0,0 +1,1967 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: routeguide.proto + +package com.baeldung.protobuf; + +public final class AddressBookProtos { + private AddressBookProtos() { + } + + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistryLite registry) { + } + + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + registerAllExtensions( + (com.google.protobuf.ExtensionRegistryLite) registry); + } + + public interface PersonOrBuilder extends + // @@protoc_insertion_point(interface_extends:protobuf.Person) + com.google.protobuf.MessageOrBuilder { + + /** + * required string name = 1; + */ + boolean hasName(); + + /** + * required string name = 1; + */ + java.lang.String getName(); + + /** + * required string name = 1; + */ + com.google.protobuf.ByteString + getNameBytes(); + + /** + * required int32 id = 2; + */ + boolean hasId(); + + /** + * required int32 id = 2; + */ + int getId(); + + /** + * optional string email = 3; + */ + boolean hasEmail(); + + /** + * optional string email = 3; + */ + java.lang.String getEmail(); + + /** + * optional string email = 3; + */ + com.google.protobuf.ByteString + getEmailBytes(); + + /** + * repeated string numbers = 4; + */ + java.util.List + getNumbersList(); + + /** + * repeated string numbers = 4; + */ + int getNumbersCount(); + + /** + * repeated string numbers = 4; + */ + java.lang.String getNumbers(int index); + + /** + * repeated string numbers = 4; + */ + com.google.protobuf.ByteString + getNumbersBytes(int index); + } + + /** + * Protobuf type {@code protobuf.Person} + */ + public static final class Person extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:protobuf.Person) + PersonOrBuilder { + // Use Person.newBuilder() to construct. + private Person(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + + private Person() { + name_ = ""; + id_ = 0; + email_ = ""; + numbers_ = com.google.protobuf.LazyStringArrayList.EMPTY; + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + + private Person( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000001; + name_ = bs; + break; + } + case 16: { + bitField0_ |= 0x00000002; + id_ = input.readInt32(); + break; + } + case 26: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000004; + email_ = bs; + break; + } + case 34: { + com.google.protobuf.ByteString bs = input.readBytes(); + if (!((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + numbers_ = new com.google.protobuf.LazyStringArrayList(); + mutable_bitField0_ |= 0x00000008; + } + numbers_.add(bs); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + numbers_ = numbers_.getUnmodifiableView(); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return com.baeldung.protobuf.AddressBookProtos.internal_static_protobuf_Person_descriptor; + } + + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.baeldung.protobuf.AddressBookProtos.internal_static_protobuf_Person_fieldAccessorTable + .ensureFieldAccessorsInitialized( + com.baeldung.protobuf.AddressBookProtos.Person.class, com.baeldung.protobuf.AddressBookProtos.Person.Builder.class); + } + + private int bitField0_; + public static final int NAME_FIELD_NUMBER = 1; + private volatile java.lang.Object name_; + + /** + * required string name = 1; + */ + public boolean hasName() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + + /** + * required string name = 1; + */ + public java.lang.String getName() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + name_ = s; + } + return s; + } + } + + /** + * required string name = 1; + */ + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int ID_FIELD_NUMBER = 2; + private int id_; + + /** + * required int32 id = 2; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + + /** + * required int32 id = 2; + */ + public int getId() { + return id_; + } + + public static final int EMAIL_FIELD_NUMBER = 3; + private volatile java.lang.Object email_; + + /** + * optional string email = 3; + */ + public boolean hasEmail() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + + /** + * optional string email = 3; + */ + public java.lang.String getEmail() { + java.lang.Object ref = email_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + email_ = s; + } + return s; + } + } + + /** + * optional string email = 3; + */ + public com.google.protobuf.ByteString + getEmailBytes() { + java.lang.Object ref = email_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + email_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int NUMBERS_FIELD_NUMBER = 4; + private com.google.protobuf.LazyStringList numbers_; + + /** + * repeated string numbers = 4; + */ + public com.google.protobuf.ProtocolStringList + getNumbersList() { + return numbers_; + } + + /** + * repeated string numbers = 4; + */ + public int getNumbersCount() { + return numbers_.size(); + } + + /** + * repeated string numbers = 4; + */ + public java.lang.String getNumbers(int index) { + return numbers_.get(index); + } + + /** + * repeated string numbers = 4; + */ + public com.google.protobuf.ByteString + getNumbersBytes(int index) { + return numbers_.getByteString(index); + } + + private byte memoizedIsInitialized = -1; + + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + if (!hasName()) { + memoizedIsInitialized = 0; + return false; + } + if (!hasId()) { + memoizedIsInitialized = 0; + return false; + } + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (((bitField0_ & 0x00000001) == 0x00000001)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 1, name_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeInt32(2, id_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 3, email_); + } + for (int i = 0; i < numbers_.size(); i++) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 4, numbers_.getRaw(i)); + } + unknownFields.writeTo(output); + } + + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, name_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(2, id_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(3, email_); + } + { + int dataSize = 0; + for (int i = 0; i < numbers_.size(); i++) { + dataSize += computeStringSizeNoTag(numbers_.getRaw(i)); + } + size += dataSize; + size += 1 * getNumbersList().size(); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof com.baeldung.protobuf.AddressBookProtos.Person)) { + return super.equals(obj); + } + com.baeldung.protobuf.AddressBookProtos.Person other = (com.baeldung.protobuf.AddressBookProtos.Person) obj; + + boolean result = true; + result = result && (hasName() == other.hasName()); + if (hasName()) { + result = result && getName() + .equals(other.getName()); + } + result = result && (hasId() == other.hasId()); + if (hasId()) { + result = result && (getId() + == other.getId()); + } + result = result && (hasEmail() == other.hasEmail()); + if (hasEmail()) { + result = result && getEmail() + .equals(other.getEmail()); + } + result = result && getNumbersList() + .equals(other.getNumbersList()); + result = result && unknownFields.equals(other.unknownFields); + return result; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptorForType().hashCode(); + if (hasName()) { + hash = (37 * hash) + NAME_FIELD_NUMBER; + hash = (53 * hash) + getName().hashCode(); + } + if (hasId()) { + hash = (37 * hash) + ID_FIELD_NUMBER; + hash = (53 * hash) + getId(); + } + if (hasEmail()) { + hash = (37 * hash) + EMAIL_FIELD_NUMBER; + hash = (53 * hash) + getEmail().hashCode(); + } + if (getNumbersCount() > 0) { + hash = (37 * hash) + NUMBERS_FIELD_NUMBER; + hash = (53 * hash) + getNumbersList().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static com.baeldung.protobuf.AddressBookProtos.Person parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static com.baeldung.protobuf.AddressBookProtos.Person parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static com.baeldung.protobuf.AddressBookProtos.Person parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static com.baeldung.protobuf.AddressBookProtos.Person parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static com.baeldung.protobuf.AddressBookProtos.Person parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + + public static com.baeldung.protobuf.AddressBookProtos.Person parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static com.baeldung.protobuf.AddressBookProtos.Person parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + + public static com.baeldung.protobuf.AddressBookProtos.Person parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + + public static com.baeldung.protobuf.AddressBookProtos.Person parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + + public static com.baeldung.protobuf.AddressBookProtos.Person parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public Builder newBuilderForType() { + return newBuilder(); + } + + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + + public static Builder newBuilder(com.baeldung.protobuf.AddressBookProtos.Person prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + + /** + * Protobuf type {@code protobuf.Person} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:protobuf.Person) + com.baeldung.protobuf.AddressBookProtos.PersonOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return com.baeldung.protobuf.AddressBookProtos.internal_static_protobuf_Person_descriptor; + } + + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.baeldung.protobuf.AddressBookProtos.internal_static_protobuf_Person_fieldAccessorTable + .ensureFieldAccessorsInitialized( + com.baeldung.protobuf.AddressBookProtos.Person.class, com.baeldung.protobuf.AddressBookProtos.Person.Builder.class); + } + + // Construct using com.baeldung.protobuf.AddressBookProtos.Person.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + + public Builder clear() { + super.clear(); + name_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + id_ = 0; + bitField0_ = (bitField0_ & ~0x00000002); + email_ = ""; + bitField0_ = (bitField0_ & ~0x00000004); + numbers_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + return this; + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return com.baeldung.protobuf.AddressBookProtos.internal_static_protobuf_Person_descriptor; + } + + public com.baeldung.protobuf.AddressBookProtos.Person getDefaultInstanceForType() { + return com.baeldung.protobuf.AddressBookProtos.Person.getDefaultInstance(); + } + + public com.baeldung.protobuf.AddressBookProtos.Person build() { + com.baeldung.protobuf.AddressBookProtos.Person result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public com.baeldung.protobuf.AddressBookProtos.Person buildPartial() { + com.baeldung.protobuf.AddressBookProtos.Person result = new com.baeldung.protobuf.AddressBookProtos.Person(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.name_ = name_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.id_ = id_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.email_ = email_; + if (((bitField0_ & 0x00000008) == 0x00000008)) { + numbers_ = numbers_.getUnmodifiableView(); + bitField0_ = (bitField0_ & ~0x00000008); + } + result.numbers_ = numbers_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder clone() { + return (Builder) super.clone(); + } + + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + Object value) { + return (Builder) super.setField(field, value); + } + + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return (Builder) super.clearField(field); + } + + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return (Builder) super.clearOneof(oneof); + } + + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, Object value) { + return (Builder) super.setRepeatedField(field, index, value); + } + + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + Object value) { + return (Builder) super.addRepeatedField(field, value); + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof com.baeldung.protobuf.AddressBookProtos.Person) { + return mergeFrom((com.baeldung.protobuf.AddressBookProtos.Person) other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(com.baeldung.protobuf.AddressBookProtos.Person other) { + if (other == com.baeldung.protobuf.AddressBookProtos.Person.getDefaultInstance()) return this; + if (other.hasName()) { + bitField0_ |= 0x00000001; + name_ = other.name_; + onChanged(); + } + if (other.hasId()) { + setId(other.getId()); + } + if (other.hasEmail()) { + bitField0_ |= 0x00000004; + email_ = other.email_; + onChanged(); + } + if (!other.numbers_.isEmpty()) { + if (numbers_.isEmpty()) { + numbers_ = other.numbers_; + bitField0_ = (bitField0_ & ~0x00000008); + } else { + ensureNumbersIsMutable(); + numbers_.addAll(other.numbers_); + } + onChanged(); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + public final boolean isInitialized() { + if (!hasName()) { + return false; + } + if (!hasId()) { + return false; + } + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + com.baeldung.protobuf.AddressBookProtos.Person parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (com.baeldung.protobuf.AddressBookProtos.Person) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + + private int bitField0_; + + private java.lang.Object name_ = ""; + + /** + * required string name = 1; + */ + public boolean hasName() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + + /** + * required string name = 1; + */ + public java.lang.String getName() { + java.lang.Object ref = name_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + name_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + + /** + * required string name = 1; + */ + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + /** + * required string name = 1; + */ + public Builder setName( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + name_ = value; + onChanged(); + return this; + } + + /** + * required string name = 1; + */ + public Builder clearName() { + bitField0_ = (bitField0_ & ~0x00000001); + name_ = getDefaultInstance().getName(); + onChanged(); + return this; + } + + /** + * required string name = 1; + */ + public Builder setNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + name_ = value; + onChanged(); + return this; + } + + private int id_; + + /** + * required int32 id = 2; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + + /** + * required int32 id = 2; + */ + public int getId() { + return id_; + } + + /** + * required int32 id = 2; + */ + public Builder setId(int value) { + bitField0_ |= 0x00000002; + id_ = value; + onChanged(); + return this; + } + + /** + * required int32 id = 2; + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000002); + id_ = 0; + onChanged(); + return this; + } + + private java.lang.Object email_ = ""; + + /** + * optional string email = 3; + */ + public boolean hasEmail() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + + /** + * optional string email = 3; + */ + public java.lang.String getEmail() { + java.lang.Object ref = email_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + email_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + + /** + * optional string email = 3; + */ + public com.google.protobuf.ByteString + getEmailBytes() { + java.lang.Object ref = email_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + email_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + /** + * optional string email = 3; + */ + public Builder setEmail( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + email_ = value; + onChanged(); + return this; + } + + /** + * optional string email = 3; + */ + public Builder clearEmail() { + bitField0_ = (bitField0_ & ~0x00000004); + email_ = getDefaultInstance().getEmail(); + onChanged(); + return this; + } + + /** + * optional string email = 3; + */ + public Builder setEmailBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + email_ = value; + onChanged(); + return this; + } + + private com.google.protobuf.LazyStringList numbers_ = com.google.protobuf.LazyStringArrayList.EMPTY; + + private void ensureNumbersIsMutable() { + if (!((bitField0_ & 0x00000008) == 0x00000008)) { + numbers_ = new com.google.protobuf.LazyStringArrayList(numbers_); + bitField0_ |= 0x00000008; + } + } + + /** + * repeated string numbers = 4; + */ + public com.google.protobuf.ProtocolStringList + getNumbersList() { + return numbers_.getUnmodifiableView(); + } + + /** + * repeated string numbers = 4; + */ + public int getNumbersCount() { + return numbers_.size(); + } + + /** + * repeated string numbers = 4; + */ + public java.lang.String getNumbers(int index) { + return numbers_.get(index); + } + + /** + * repeated string numbers = 4; + */ + public com.google.protobuf.ByteString + getNumbersBytes(int index) { + return numbers_.getByteString(index); + } + + /** + * repeated string numbers = 4; + */ + public Builder setNumbers( + int index, java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureNumbersIsMutable(); + numbers_.set(index, value); + onChanged(); + return this; + } + + /** + * repeated string numbers = 4; + */ + public Builder addNumbers( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureNumbersIsMutable(); + numbers_.add(value); + onChanged(); + return this; + } + + /** + * repeated string numbers = 4; + */ + public Builder addAllNumbers( + java.lang.Iterable values) { + ensureNumbersIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, numbers_); + onChanged(); + return this; + } + + /** + * repeated string numbers = 4; + */ + public Builder clearNumbers() { + numbers_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + onChanged(); + return this; + } + + /** + * repeated string numbers = 4; + */ + public Builder addNumbersBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + ensureNumbersIsMutable(); + numbers_.add(value); + onChanged(); + return this; + } + + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:protobuf.Person) + } + + // @@protoc_insertion_point(class_scope:protobuf.Person) + private static final com.baeldung.protobuf.AddressBookProtos.Person DEFAULT_INSTANCE; + + static { + DEFAULT_INSTANCE = new com.baeldung.protobuf.AddressBookProtos.Person(); + } + + public static com.baeldung.protobuf.AddressBookProtos.Person getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + @java.lang.Deprecated + public static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + public Person parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Person(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + public com.baeldung.protobuf.AddressBookProtos.Person getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + public interface AddressBookOrBuilder extends + // @@protoc_insertion_point(interface_extends:protobuf.AddressBook) + com.google.protobuf.MessageOrBuilder { + + /** + * repeated .protobuf.Person people = 1; + */ + java.util.List + getPeopleList(); + + /** + * repeated .protobuf.Person people = 1; + */ + com.baeldung.protobuf.AddressBookProtos.Person getPeople(int index); + + /** + * repeated .protobuf.Person people = 1; + */ + int getPeopleCount(); + + /** + * repeated .protobuf.Person people = 1; + */ + java.util.List + getPeopleOrBuilderList(); + + /** + * repeated .protobuf.Person people = 1; + */ + com.baeldung.protobuf.AddressBookProtos.PersonOrBuilder getPeopleOrBuilder( + int index); + } + + /** + * Protobuf type {@code protobuf.AddressBook} + */ + public static final class AddressBook extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:protobuf.AddressBook) + AddressBookOrBuilder { + // Use AddressBook.newBuilder() to construct. + private AddressBook(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + + private AddressBook() { + people_ = java.util.Collections.emptyList(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + + private AddressBook( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + if (!((mutable_bitField0_ & 0x00000001) == 0x00000001)) { + people_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000001; + } + people_.add( + input.readMessage(com.baeldung.protobuf.AddressBookProtos.Person.PARSER, extensionRegistry)); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000001) == 0x00000001)) { + people_ = java.util.Collections.unmodifiableList(people_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return com.baeldung.protobuf.AddressBookProtos.internal_static_protobuf_AddressBook_descriptor; + } + + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.baeldung.protobuf.AddressBookProtos.internal_static_protobuf_AddressBook_fieldAccessorTable + .ensureFieldAccessorsInitialized( + com.baeldung.protobuf.AddressBookProtos.AddressBook.class, com.baeldung.protobuf.AddressBookProtos.AddressBook.Builder.class); + } + + public static final int PEOPLE_FIELD_NUMBER = 1; + private java.util.List people_; + + /** + * repeated .protobuf.Person people = 1; + */ + public java.util.List getPeopleList() { + return people_; + } + + /** + * repeated .protobuf.Person people = 1; + */ + public java.util.List + getPeopleOrBuilderList() { + return people_; + } + + /** + * repeated .protobuf.Person people = 1; + */ + public int getPeopleCount() { + return people_.size(); + } + + /** + * repeated .protobuf.Person people = 1; + */ + public com.baeldung.protobuf.AddressBookProtos.Person getPeople(int index) { + return people_.get(index); + } + + /** + * repeated .protobuf.Person people = 1; + */ + public com.baeldung.protobuf.AddressBookProtos.PersonOrBuilder getPeopleOrBuilder( + int index) { + return people_.get(index); + } + + private byte memoizedIsInitialized = -1; + + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + for (int i = 0; i < getPeopleCount(); i++) { + if (!getPeople(i).isInitialized()) { + memoizedIsInitialized = 0; + return false; + } + } + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + for (int i = 0; i < people_.size(); i++) { + output.writeMessage(1, people_.get(i)); + } + unknownFields.writeTo(output); + } + + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + for (int i = 0; i < people_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, people_.get(i)); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof com.baeldung.protobuf.AddressBookProtos.AddressBook)) { + return super.equals(obj); + } + com.baeldung.protobuf.AddressBookProtos.AddressBook other = (com.baeldung.protobuf.AddressBookProtos.AddressBook) obj; + + boolean result = true; + result = result && getPeopleList() + .equals(other.getPeopleList()); + result = result && unknownFields.equals(other.unknownFields); + return result; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptorForType().hashCode(); + if (getPeopleCount() > 0) { + hash = (37 * hash) + PEOPLE_FIELD_NUMBER; + hash = (53 * hash) + getPeopleList().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static com.baeldung.protobuf.AddressBookProtos.AddressBook parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static com.baeldung.protobuf.AddressBookProtos.AddressBook parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static com.baeldung.protobuf.AddressBookProtos.AddressBook parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static com.baeldung.protobuf.AddressBookProtos.AddressBook parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static com.baeldung.protobuf.AddressBookProtos.AddressBook parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + + public static com.baeldung.protobuf.AddressBookProtos.AddressBook parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static com.baeldung.protobuf.AddressBookProtos.AddressBook parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + + public static com.baeldung.protobuf.AddressBookProtos.AddressBook parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + + public static com.baeldung.protobuf.AddressBookProtos.AddressBook parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + + public static com.baeldung.protobuf.AddressBookProtos.AddressBook parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public Builder newBuilderForType() { + return newBuilder(); + } + + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + + public static Builder newBuilder(com.baeldung.protobuf.AddressBookProtos.AddressBook prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + + /** + * Protobuf type {@code protobuf.AddressBook} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:protobuf.AddressBook) + com.baeldung.protobuf.AddressBookProtos.AddressBookOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return com.baeldung.protobuf.AddressBookProtos.internal_static_protobuf_AddressBook_descriptor; + } + + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.baeldung.protobuf.AddressBookProtos.internal_static_protobuf_AddressBook_fieldAccessorTable + .ensureFieldAccessorsInitialized( + com.baeldung.protobuf.AddressBookProtos.AddressBook.class, com.baeldung.protobuf.AddressBookProtos.AddressBook.Builder.class); + } + + // Construct using com.baeldung.protobuf.AddressBookProtos.AddressBook.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + getPeopleFieldBuilder(); + } + } + + public Builder clear() { + super.clear(); + if (peopleBuilder_ == null) { + people_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + } else { + peopleBuilder_.clear(); + } + return this; + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return com.baeldung.protobuf.AddressBookProtos.internal_static_protobuf_AddressBook_descriptor; + } + + public com.baeldung.protobuf.AddressBookProtos.AddressBook getDefaultInstanceForType() { + return com.baeldung.protobuf.AddressBookProtos.AddressBook.getDefaultInstance(); + } + + public com.baeldung.protobuf.AddressBookProtos.AddressBook build() { + com.baeldung.protobuf.AddressBookProtos.AddressBook result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public com.baeldung.protobuf.AddressBookProtos.AddressBook buildPartial() { + com.baeldung.protobuf.AddressBookProtos.AddressBook result = new com.baeldung.protobuf.AddressBookProtos.AddressBook(this); + int from_bitField0_ = bitField0_; + if (peopleBuilder_ == null) { + if (((bitField0_ & 0x00000001) == 0x00000001)) { + people_ = java.util.Collections.unmodifiableList(people_); + bitField0_ = (bitField0_ & ~0x00000001); + } + result.people_ = people_; + } else { + result.people_ = peopleBuilder_.build(); + } + onBuilt(); + return result; + } + + public Builder clone() { + return (Builder) super.clone(); + } + + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + Object value) { + return (Builder) super.setField(field, value); + } + + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return (Builder) super.clearField(field); + } + + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return (Builder) super.clearOneof(oneof); + } + + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, Object value) { + return (Builder) super.setRepeatedField(field, index, value); + } + + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + Object value) { + return (Builder) super.addRepeatedField(field, value); + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof com.baeldung.protobuf.AddressBookProtos.AddressBook) { + return mergeFrom((com.baeldung.protobuf.AddressBookProtos.AddressBook) other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(com.baeldung.protobuf.AddressBookProtos.AddressBook other) { + if (other == com.baeldung.protobuf.AddressBookProtos.AddressBook.getDefaultInstance()) return this; + if (peopleBuilder_ == null) { + if (!other.people_.isEmpty()) { + if (people_.isEmpty()) { + people_ = other.people_; + bitField0_ = (bitField0_ & ~0x00000001); + } else { + ensurePeopleIsMutable(); + people_.addAll(other.people_); + } + onChanged(); + } + } else { + if (!other.people_.isEmpty()) { + if (peopleBuilder_.isEmpty()) { + peopleBuilder_.dispose(); + peopleBuilder_ = null; + people_ = other.people_; + bitField0_ = (bitField0_ & ~0x00000001); + peopleBuilder_ = + com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ? + getPeopleFieldBuilder() : null; + } else { + peopleBuilder_.addAllMessages(other.people_); + } + } + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + public final boolean isInitialized() { + for (int i = 0; i < getPeopleCount(); i++) { + if (!getPeople(i).isInitialized()) { + return false; + } + } + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + com.baeldung.protobuf.AddressBookProtos.AddressBook parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (com.baeldung.protobuf.AddressBookProtos.AddressBook) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + + private int bitField0_; + + private java.util.List people_ = + java.util.Collections.emptyList(); + + private void ensurePeopleIsMutable() { + if (!((bitField0_ & 0x00000001) == 0x00000001)) { + people_ = new java.util.ArrayList(people_); + bitField0_ |= 0x00000001; + } + } + + private com.google.protobuf.RepeatedFieldBuilderV3< + com.baeldung.protobuf.AddressBookProtos.Person, com.baeldung.protobuf.AddressBookProtos.Person.Builder, com.baeldung.protobuf.AddressBookProtos.PersonOrBuilder> peopleBuilder_; + + /** + * repeated .protobuf.Person people = 1; + */ + public java.util.List getPeopleList() { + if (peopleBuilder_ == null) { + return java.util.Collections.unmodifiableList(people_); + } else { + return peopleBuilder_.getMessageList(); + } + } + + /** + * repeated .protobuf.Person people = 1; + */ + public int getPeopleCount() { + if (peopleBuilder_ == null) { + return people_.size(); + } else { + return peopleBuilder_.getCount(); + } + } + + /** + * repeated .protobuf.Person people = 1; + */ + public com.baeldung.protobuf.AddressBookProtos.Person getPeople(int index) { + if (peopleBuilder_ == null) { + return people_.get(index); + } else { + return peopleBuilder_.getMessage(index); + } + } + + /** + * repeated .protobuf.Person people = 1; + */ + public Builder setPeople( + int index, com.baeldung.protobuf.AddressBookProtos.Person value) { + if (peopleBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensurePeopleIsMutable(); + people_.set(index, value); + onChanged(); + } else { + peopleBuilder_.setMessage(index, value); + } + return this; + } + + /** + * repeated .protobuf.Person people = 1; + */ + public Builder setPeople( + int index, com.baeldung.protobuf.AddressBookProtos.Person.Builder builderForValue) { + if (peopleBuilder_ == null) { + ensurePeopleIsMutable(); + people_.set(index, builderForValue.build()); + onChanged(); + } else { + peopleBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + + /** + * repeated .protobuf.Person people = 1; + */ + public Builder addPeople(com.baeldung.protobuf.AddressBookProtos.Person value) { + if (peopleBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensurePeopleIsMutable(); + people_.add(value); + onChanged(); + } else { + peopleBuilder_.addMessage(value); + } + return this; + } + + /** + * repeated .protobuf.Person people = 1; + */ + public Builder addPeople( + int index, com.baeldung.protobuf.AddressBookProtos.Person value) { + if (peopleBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensurePeopleIsMutable(); + people_.add(index, value); + onChanged(); + } else { + peopleBuilder_.addMessage(index, value); + } + return this; + } + + /** + * repeated .protobuf.Person people = 1; + */ + public Builder addPeople( + com.baeldung.protobuf.AddressBookProtos.Person.Builder builderForValue) { + if (peopleBuilder_ == null) { + ensurePeopleIsMutable(); + people_.add(builderForValue.build()); + onChanged(); + } else { + peopleBuilder_.addMessage(builderForValue.build()); + } + return this; + } + + /** + * repeated .protobuf.Person people = 1; + */ + public Builder addPeople( + int index, com.baeldung.protobuf.AddressBookProtos.Person.Builder builderForValue) { + if (peopleBuilder_ == null) { + ensurePeopleIsMutable(); + people_.add(index, builderForValue.build()); + onChanged(); + } else { + peopleBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + + /** + * repeated .protobuf.Person people = 1; + */ + public Builder addAllPeople( + java.lang.Iterable values) { + if (peopleBuilder_ == null) { + ensurePeopleIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, people_); + onChanged(); + } else { + peopleBuilder_.addAllMessages(values); + } + return this; + } + + /** + * repeated .protobuf.Person people = 1; + */ + public Builder clearPeople() { + if (peopleBuilder_ == null) { + people_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + } else { + peopleBuilder_.clear(); + } + return this; + } + + /** + * repeated .protobuf.Person people = 1; + */ + public Builder removePeople(int index) { + if (peopleBuilder_ == null) { + ensurePeopleIsMutable(); + people_.remove(index); + onChanged(); + } else { + peopleBuilder_.remove(index); + } + return this; + } + + /** + * repeated .protobuf.Person people = 1; + */ + public com.baeldung.protobuf.AddressBookProtos.Person.Builder getPeopleBuilder( + int index) { + return getPeopleFieldBuilder().getBuilder(index); + } + + /** + * repeated .protobuf.Person people = 1; + */ + public com.baeldung.protobuf.AddressBookProtos.PersonOrBuilder getPeopleOrBuilder( + int index) { + if (peopleBuilder_ == null) { + return people_.get(index); + } else { + return peopleBuilder_.getMessageOrBuilder(index); + } + } + + /** + * repeated .protobuf.Person people = 1; + */ + public java.util.List + getPeopleOrBuilderList() { + if (peopleBuilder_ != null) { + return peopleBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(people_); + } + } + + /** + * repeated .protobuf.Person people = 1; + */ + public com.baeldung.protobuf.AddressBookProtos.Person.Builder addPeopleBuilder() { + return getPeopleFieldBuilder().addBuilder( + com.baeldung.protobuf.AddressBookProtos.Person.getDefaultInstance()); + } + + /** + * repeated .protobuf.Person people = 1; + */ + public com.baeldung.protobuf.AddressBookProtos.Person.Builder addPeopleBuilder( + int index) { + return getPeopleFieldBuilder().addBuilder( + index, com.baeldung.protobuf.AddressBookProtos.Person.getDefaultInstance()); + } + + /** + * repeated .protobuf.Person people = 1; + */ + public java.util.List + getPeopleBuilderList() { + return getPeopleFieldBuilder().getBuilderList(); + } + + private com.google.protobuf.RepeatedFieldBuilderV3< + com.baeldung.protobuf.AddressBookProtos.Person, com.baeldung.protobuf.AddressBookProtos.Person.Builder, com.baeldung.protobuf.AddressBookProtos.PersonOrBuilder> + getPeopleFieldBuilder() { + if (peopleBuilder_ == null) { + peopleBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3< + com.baeldung.protobuf.AddressBookProtos.Person, com.baeldung.protobuf.AddressBookProtos.Person.Builder, com.baeldung.protobuf.AddressBookProtos.PersonOrBuilder>( + people_, + ((bitField0_ & 0x00000001) == 0x00000001), + getParentForChildren(), + isClean()); + people_ = null; + } + return peopleBuilder_; + } + + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:protobuf.AddressBook) + } + + // @@protoc_insertion_point(class_scope:protobuf.AddressBook) + private static final com.baeldung.protobuf.AddressBookProtos.AddressBook DEFAULT_INSTANCE; + + static { + DEFAULT_INSTANCE = new com.baeldung.protobuf.AddressBookProtos.AddressBook(); + } + + public static com.baeldung.protobuf.AddressBookProtos.AddressBook getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + @java.lang.Deprecated + public static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + public AddressBook parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new AddressBook(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + public com.baeldung.protobuf.AddressBookProtos.AddressBook getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_protobuf_Person_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_protobuf_Person_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_protobuf_AddressBook_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_protobuf_AddressBook_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + + static { + java.lang.String[] descriptorData = { + "\n\020routeguide.proto\022\010protobuf\"B\n\006Person\022\014" + + "\n\004name\030\001 \002(\t\022\n\n\002id\030\002 \002(\005\022\r\n\005email\030\003 \001(\t\022" + + "\017\n\007numbers\030\004 \003(\t\"/\n\013AddressBook\022 \n\006peopl" + + "e\030\001 \003(\0132\020.protobuf.PersonB*\n\025com.baeldun" + + "g.protobufB\021AddressBookProtos" + }; + com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = + new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { + public com.google.protobuf.ExtensionRegistry assignDescriptors( + com.google.protobuf.Descriptors.FileDescriptor root) { + descriptor = root; + return null; + } + }; + com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[]{ + }, assigner); + internal_static_protobuf_Person_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_protobuf_Person_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_protobuf_Person_descriptor, + new java.lang.String[]{"Name", "Id", "Email", "Numbers",}); + internal_static_protobuf_AddressBook_descriptor = + getDescriptor().getMessageTypes().get(1); + internal_static_protobuf_AddressBook_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_protobuf_AddressBook_descriptor, + new java.lang.String[]{"People",}); + } + +// @@protoc_insertion_point(outer_class_scope) +} \ No newline at end of file diff --git a/protobuffer/src/main/resources/addressbook.proto b/protobuffer/src/main/resources/addressbook.proto new file mode 100644 index 000000000000..fe3f9c4174ec --- /dev/null +++ b/protobuffer/src/main/resources/addressbook.proto @@ -0,0 +1,16 @@ +package protobuf; + +option java_package = "com.baeldung.protobuf"; +option java_outer_classname = "AddressBookProtos"; + +message Person { + required string name = 1; + required int32 id = 2; + optional string email = 3; + + repeated string numbers = 4; +} + +message AddressBook { + repeated Person people = 1; +} \ No newline at end of file diff --git a/protobuffer/src/test/java/com/baeldung/protobuf/ProtobufTest.java b/protobuffer/src/test/java/com/baeldung/protobuf/ProtobufTest.java new file mode 100644 index 000000000000..f0d64092f054 --- /dev/null +++ b/protobuffer/src/test/java/com/baeldung/protobuf/ProtobufTest.java @@ -0,0 +1,79 @@ +package com.baeldung.protobuf; + + +import org.junit.After; +import org.junit.Test; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Random; + +import static org.junit.Assert.assertEquals; + +public class ProtobufTest { + private final String filePath = "address_book"; + + @After + public void cleanup() throws IOException { + Files.deleteIfExists(Paths.get(filePath)); + } + + @Test + public void givenGeneratedProtobufClass_whenCreateClass_thenShouldCreateJavaInstance() { + //when + String email = "j@baeldung.com"; + int id = new Random().nextInt(); + String name = "Michael Program"; + String number = "01234567890"; + AddressBookProtos.Person person = + AddressBookProtos.Person.newBuilder() + .setId(id) + .setName(name) + .setEmail(email) + .addNumbers(number) + .build(); + //then + assertEquals(person.getEmail(), email); + assertEquals(person.getId(), id); + assertEquals(person.getName(), name); + assertEquals(person.getNumbers(0), number); + } + + + @Test + public void givenAddressBookWithOnePerson_whenSaveAsAFile_shouldLoadFromFileToJavaClass() throws IOException { + //given + String email = "j@baeldung.com"; + int id = new Random().nextInt(); + String name = "Michael Program"; + String number = "01234567890"; + AddressBookProtos.Person person = + AddressBookProtos.Person.newBuilder() + .setId(id) + .setName(name) + .setEmail(email) + .addNumbers(number) + .build(); + + //when + AddressBookProtos.AddressBook addressBook = AddressBookProtos.AddressBook.newBuilder().addPeople(person).build(); + FileOutputStream fos = new FileOutputStream(filePath); + addressBook.writeTo(fos); + fos.close(); + + //then + FileInputStream fis = new FileInputStream(filePath); + AddressBookProtos.AddressBook deserialized = + AddressBookProtos.AddressBook.newBuilder().mergeFrom(fis).build(); + fis.close(); + assertEquals(deserialized.getPeople(0).getEmail(), email); + assertEquals(deserialized.getPeople(0).getId(), id); + assertEquals(deserialized.getPeople(0).getName(), name); + assertEquals(deserialized.getPeople(0).getNumbers(0), number); + + + } +} diff --git a/querydsl/pom.xml b/querydsl/pom.xml index a9218e775190..d6fe7f419153 100644 --- a/querydsl/pom.xml +++ b/querydsl/pom.xml @@ -91,6 +91,12 @@ org.springframework spring-context ${spring.version} + + + commons-logging + commons-logging + + @@ -180,8 +186,9 @@ org.apache.maven.plugins maven-surefire-plugin - ${maven-surefire-plugin.version} + 3 + true **/*IntegrationTest.java **/*LiveTest.java diff --git a/querydsl/src/main/resources/logback.xml b/querydsl/src/main/resources/logback.xml index d0a1dc06ac4a..ec0dc2469ae0 100644 --- a/querydsl/src/main/resources/logback.xml +++ b/querydsl/src/main/resources/logback.xml @@ -1,37 +1,19 @@ - - - %d{yyyy-MM-dd HH:mm:ss} [%t] %-5p: %c - %m%n - + + web - %date [%thread] %-5level %logger{36} - %message%n + + - - - - - - - - - - - - - + + - - - + + - - - - - - - + + - \ No newline at end of file diff --git a/querydsl/src/test/java/org/baeldung/querydsl/intro/QueryDSLTest.java b/querydsl/src/test/java/org/baeldung/querydsl/intro/QueryDSLIntegrationTest.java similarity index 99% rename from querydsl/src/test/java/org/baeldung/querydsl/intro/QueryDSLTest.java rename to querydsl/src/test/java/org/baeldung/querydsl/intro/QueryDSLIntegrationTest.java index 682fa2c2456e..15a32b8df521 100644 --- a/querydsl/src/test/java/org/baeldung/querydsl/intro/QueryDSLTest.java +++ b/querydsl/src/test/java/org/baeldung/querydsl/intro/QueryDSLIntegrationTest.java @@ -3,25 +3,25 @@ */ package org.baeldung.querydsl.intro; -import java.util.List; -import javax.persistence.EntityManager; -import javax.persistence.EntityManagerFactory; -import javax.persistence.Persistence; - -import org.baeldung.querydsl.intro.entities.BlogPost; -import org.baeldung.querydsl.intro.entities.QBlogPost; -import org.baeldung.querydsl.intro.entities.QUser; -import org.baeldung.querydsl.intro.entities.User; import com.querydsl.core.Tuple; import com.querydsl.core.types.dsl.Expressions; import com.querydsl.core.types.dsl.NumberPath; import com.querydsl.jpa.JPAExpressions; import com.querydsl.jpa.impl.JPAQueryFactory; +import org.baeldung.querydsl.intro.entities.BlogPost; +import org.baeldung.querydsl.intro.entities.QBlogPost; +import org.baeldung.querydsl.intro.entities.QUser; +import org.baeldung.querydsl.intro.entities.User; import org.junit.*; +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.Persistence; +import java.util.List; + import static org.junit.Assert.*; -public class QueryDSLTest { +public class QueryDSLIntegrationTest { private static EntityManagerFactory emf; diff --git a/rabbitmq/README.md b/rabbitmq/README.md new file mode 100644 index 000000000000..11300b047f74 --- /dev/null +++ b/rabbitmq/README.md @@ -0,0 +1,3 @@ +### Relevant articles +- [Introduction to RabbitMQ](http://www.baeldung.com/rabbitmq) + diff --git a/ratpack/README.md b/ratpack/README.md new file mode 100644 index 000000000000..91c8e025f0ee --- /dev/null +++ b/ratpack/README.md @@ -0,0 +1,4 @@ +### Relevant articles + +- [Introduction to Ratpack](http://www.baeldung.com/ratpack) +- [Ratpack Google Guice Integration](http://www.baeldung.com/ratpack-google-guice) diff --git a/ratpack/build.gradle b/ratpack/build.gradle new file mode 100644 index 000000000000..aeddd5f9f992 --- /dev/null +++ b/ratpack/build.gradle @@ -0,0 +1,35 @@ +buildscript { + repositories { + jcenter() + } + dependencies { + classpath "io.ratpack:ratpack-gradle:1.4.5" + classpath "com.h2database:h2:1.4.193" + } +} + +if (!JavaVersion.current().java8Compatible) { + throw new IllegalStateException("Must be built with Java 8 or higher") +} + +apply plugin: "io.ratpack.ratpack-java" +apply plugin: 'java' + +repositories { + jcenter() +} + +dependencies { + compile ratpack.dependency('hikari') + compile 'com.h2database:h2:1.4.193' + testCompile 'junit:junit:4.11' + runtime "org.slf4j:slf4j-simple:1.7.21" +} + +test { + testLogging { + events 'started', 'passed' + } +} + +mainClassName = "com.baeldung.Application" diff --git a/ratpack/pom.xml b/ratpack/pom.xml new file mode 100644 index 000000000000..bb606b9b1110 --- /dev/null +++ b/ratpack/pom.xml @@ -0,0 +1,55 @@ + + 4.0.0 + com.baeldung + ratpack + jar + 1.0-SNAPSHOT + ratpack + http://maven.apache.org + + + UTF-8 + 1.8 + 1.8 + + + + + io.ratpack + ratpack-core + 1.4.5 + + + io.ratpack + ratpack-hikari + 1.4.5 + + + io.ratpack + ratpack-test + 1.4.5 + + + com.h2database + h2 + 1.4.193 + + + junit + junit + 4.12 + test + + + + + ${project.artifactId} + + + src/main/resources + + + + + diff --git a/ratpack/src/main/java/com/baeldung/Application.java b/ratpack/src/main/java/com/baeldung/Application.java new file mode 100644 index 000000000000..7f46b241eac7 --- /dev/null +++ b/ratpack/src/main/java/com/baeldung/Application.java @@ -0,0 +1,49 @@ +package com.baeldung; + +import java.util.ArrayList; +import java.util.List; + +import com.baeldung.filter.RequestValidatorFilter; +import com.baeldung.model.Employee; + +import ratpack.guice.Guice; +import ratpack.hikari.HikariModule; +import ratpack.http.MutableHeaders; +import ratpack.jackson.Jackson; +import ratpack.http.MutableHeaders; +import ratpack.server.RatpackServer; + +public class Application { + + public static void main(String[] args) throws Exception { + + List employees = new ArrayList(); + employees.add(new Employee(1L, "Mr", "John Doe")); + employees.add(new Employee(2L, "Mr", "White Snow")); + + + RatpackServer.start( + server -> server.registry(Guice.registry(bindings -> bindings.module(HikariModule.class, config -> { + config.setDataSourceClassName("org.h2.jdbcx.JdbcDataSource"); + config.addDataSourceProperty("URL", + "jdbc:h2:mem:baeldung;INIT=RUNSCRIPT FROM 'classpath:/DDL.sql'"); + }))).handlers(chain -> chain + .all( + // ctx -> { + // MutableHeaders headers = + // ctx.getResponse().getHeaders(); + // headers.set("Access-Control-Allow-Origin","*"); + // headers.set("Accept-Language", "en-us"); + // headers.set("Accept-Charset", "UTF-8"); + // ctx.next(); + // } + new RequestValidatorFilter()) + .get(ctx -> ctx.render("Welcome to baeldung ratpack!!!")) + .get("data/employees", ctx -> ctx.render(Jackson.json(employees))) + .get(":name", ctx -> ctx.render("Hello " + ctx.getPathTokens().get("name") + "!!!")) + .post(":amount", ctx -> ctx + .render(" Amount $" + ctx.getPathTokens().get("amount") + " added successfully !!!")))); + } + +} + diff --git a/ratpack/src/main/java/com/baeldung/filter/RequestValidatorFilter.java b/ratpack/src/main/java/com/baeldung/filter/RequestValidatorFilter.java new file mode 100644 index 000000000000..0a8b77aa7175 --- /dev/null +++ b/ratpack/src/main/java/com/baeldung/filter/RequestValidatorFilter.java @@ -0,0 +1,16 @@ +package com.baeldung.filter; + +import ratpack.handling.Context; +import ratpack.handling.Handler; +import ratpack.http.MutableHeaders; + +public class RequestValidatorFilter implements Handler { + + @Override + public void handle(Context ctx) throws Exception { + MutableHeaders headers = ctx.getResponse().getHeaders(); + headers.set("Access-Control-Allow-Origin", "*"); + ctx.next(); + } + +} diff --git a/ratpack/src/main/java/com/baeldung/guice/Application.java b/ratpack/src/main/java/com/baeldung/guice/Application.java new file mode 100644 index 000000000000..4e837501da49 --- /dev/null +++ b/ratpack/src/main/java/com/baeldung/guice/Application.java @@ -0,0 +1,32 @@ +package com.baeldung.guice; + +import com.baeldung.guice.config.DependencyModule; +import com.baeldung.guice.service.DataPumpService; +import com.baeldung.guice.service.ServiceFactory; +import com.baeldung.guice.service.impl.DataPumpServiceImpl; + +import ratpack.guice.Guice; +import ratpack.server.RatpackServer; + +public class Application { + + public static void main(String[] args) throws Exception { + + RatpackServer + .start(server -> server.registry(Guice.registry(bindings -> bindings.module(DependencyModule.class))) + .handlers(chain -> chain.get("randomString", ctx -> { + DataPumpService dataPumpService = ctx.get(DataPumpService.class); + ctx.render(dataPumpService.generate()); + }).get("factory", ctx -> ctx.render(ServiceFactory.getInstance().generate())))); + +// RatpackServer.start(server -> server +// .registry(Guice +// .registry(bindings -> bindings.bindInstance(DataPumpService.class, new DataPumpServiceImpl()))) +// .handlers(chain -> chain.get("randomString", ctx -> { +// DataPumpService dataPumpService = ctx.get(DataPumpService.class); +// ctx.render(dataPumpService.generate()); +// }).get("factory", ctx -> ctx.render(ServiceFactory.getInstance().generate())))); + + } + +} \ No newline at end of file diff --git a/ratpack/src/main/java/com/baeldung/guice/config/DependencyModule.java b/ratpack/src/main/java/com/baeldung/guice/config/DependencyModule.java new file mode 100644 index 000000000000..1824578501d5 --- /dev/null +++ b/ratpack/src/main/java/com/baeldung/guice/config/DependencyModule.java @@ -0,0 +1,16 @@ +package com.baeldung.guice.config; + +import com.baeldung.guice.service.DataPumpService; +import com.baeldung.guice.service.impl.DataPumpServiceImpl; +import com.google.inject.AbstractModule; +import com.google.inject.Scopes; + +public class DependencyModule extends AbstractModule { + + @Override + protected void configure() { + bind(DataPumpService.class).to(DataPumpServiceImpl.class) + .in(Scopes.SINGLETON); + } + +} diff --git a/ratpack/src/main/java/com/baeldung/guice/service/DataPumpService.java b/ratpack/src/main/java/com/baeldung/guice/service/DataPumpService.java new file mode 100644 index 000000000000..01b7dbabef15 --- /dev/null +++ b/ratpack/src/main/java/com/baeldung/guice/service/DataPumpService.java @@ -0,0 +1,7 @@ +package com.baeldung.guice.service; + +public interface DataPumpService { + + String generate(); + +} diff --git a/ratpack/src/main/java/com/baeldung/guice/service/ServiceFactory.java b/ratpack/src/main/java/com/baeldung/guice/service/ServiceFactory.java new file mode 100644 index 000000000000..d11beb1cb2a3 --- /dev/null +++ b/ratpack/src/main/java/com/baeldung/guice/service/ServiceFactory.java @@ -0,0 +1,20 @@ +package com.baeldung.guice.service; + +import com.baeldung.guice.service.impl.DataPumpServiceImpl; + +public class ServiceFactory { + + private static DataPumpService instance; + + public static void setInstance(DataPumpService dataPumpService) { + instance = dataPumpService; + } + + public static DataPumpService getInstance() { + if (instance == null) { + return new DataPumpServiceImpl(); + } + return instance; + } + +} diff --git a/ratpack/src/main/java/com/baeldung/guice/service/impl/DataPumpServiceImpl.java b/ratpack/src/main/java/com/baeldung/guice/service/impl/DataPumpServiceImpl.java new file mode 100644 index 000000000000..88f171f8a2dc --- /dev/null +++ b/ratpack/src/main/java/com/baeldung/guice/service/impl/DataPumpServiceImpl.java @@ -0,0 +1,14 @@ +package com.baeldung.guice.service.impl; + +import java.util.UUID; + +import com.baeldung.guice.service.DataPumpService; + +public class DataPumpServiceImpl implements DataPumpService { + + @Override + public String generate() { + return UUID.randomUUID().toString(); + } + +} diff --git a/ratpack/src/main/java/com/baeldung/model/Employee.java b/ratpack/src/main/java/com/baeldung/model/Employee.java new file mode 100644 index 000000000000..c983d8b76967 --- /dev/null +++ b/ratpack/src/main/java/com/baeldung/model/Employee.java @@ -0,0 +1,44 @@ +package com.baeldung.model; + +import java.io.Serializable; + +public class Employee implements Serializable { + + private static final long serialVersionUID = 3077867088762010705L; + + private Long id; + private String title; + private String name; + + public Employee(Long id, String title, String name) { + super(); + this.id = id; + this.title = title; + this.name = name; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/ratpack/src/main/resources/DDL.sql b/ratpack/src/main/resources/DDL.sql new file mode 100644 index 000000000000..af8709ee75c0 --- /dev/null +++ b/ratpack/src/main/resources/DDL.sql @@ -0,0 +1,6 @@ +DROP TABLE IF EXISTS employee; +CREATE TABLE employee ( + id bigint auto_increment primary key, + title varchar(255), + name varchar(255) +) \ No newline at end of file diff --git a/ratpack/src/test/java/com/baeldung/ApplicationTest.java b/ratpack/src/test/java/com/baeldung/ApplicationTest.java new file mode 100644 index 000000000000..047575ca6e51 --- /dev/null +++ b/ratpack/src/test/java/com/baeldung/ApplicationTest.java @@ -0,0 +1,53 @@ +package com.baeldung; + +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import com.baeldung.model.Employee; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import ratpack.test.MainClassApplicationUnderTest; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(JUnit4.class) +public class ApplicationTest { + + MainClassApplicationUnderTest appUnderTest = new MainClassApplicationUnderTest(Application.class); + + @Test + public void givenDefaultUrl_getStaticText() { + assertEquals("Welcome to baeldung ratpack!!!", appUnderTest.getHttpClient().getText("/")); + } + + @Test + public void givenDynamicUrl_getDynamicText() { + assertEquals("Hello dummybot!!!", appUnderTest.getHttpClient().getText("/dummybot")); + } + + @Test + public void givenUrl_getListOfEmployee() throws JsonProcessingException { + List employees = new ArrayList(); + ObjectMapper mapper = new ObjectMapper(); + employees.add(new Employee(1L, "Mr", "John Doe")); + employees.add(new Employee(2L, "Mr", "White Snow")); + + assertEquals(mapper.writeValueAsString(employees), appUnderTest.getHttpClient().getText("/data/employees")); + } + + @Test + public void givenStaticUrl_getDynamicText() { + assertEquals(21, appUnderTest.getHttpClient().getText("/randomString").length()); + } + + @After + public void shutdown() { + appUnderTest.close(); + } + +} \ No newline at end of file diff --git a/reactor-core/README.md b/reactor-core/README.md new file mode 100644 index 000000000000..9d952ec84c41 --- /dev/null +++ b/reactor-core/README.md @@ -0,0 +1,3 @@ +### Relevant articles + +- [Intro To Reactor Core](http://www.baeldung.com/reactor-core) diff --git a/reactor-core/pom.xml b/reactor-core/pom.xml index 2be88929831b..3aeb4af3d59a 100644 --- a/reactor-core/pom.xml +++ b/reactor-core/pom.xml @@ -50,7 +50,7 @@ - 3.0.4.RELEASE + 3.0.5.RELEASE 4.12 3.6.1 1.1.3 diff --git a/redis/README.md b/redis/README.md index 3ce9031b628b..d179b80c33ea 100644 --- a/redis/README.md +++ b/redis/README.md @@ -1,2 +1,3 @@ ### Relevant Articles: - [Intro to Jedis – the Java Redis Client Library](http://www.baeldung.com/jedis-java-redis-client-library) +- [A Guide to Redis with Redisson](http://www.baeldung.com/redis-redisson) diff --git a/redis/pom.xml b/redis/pom.xml index e3affdd7ef00..9025294d59cc 100644 --- a/redis/pom.xml +++ b/redis/pom.xml @@ -23,6 +23,12 @@ ${embedded-redis.version} + + org.redisson + redisson + 3.3.0 + + junit junit diff --git a/redis/src/main/java/com/baeldung/CustomMessage.java b/redis/src/main/java/com/baeldung/CustomMessage.java new file mode 100644 index 000000000000..1d6a7e4e139c --- /dev/null +++ b/redis/src/main/java/com/baeldung/CustomMessage.java @@ -0,0 +1,19 @@ +package com.baeldung; + +/** + * Created by johnson on 3/9/17. + */ +public class CustomMessage { + private String message; + + public CustomMessage() { + } + + public CustomMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } +} diff --git a/redis/src/main/java/com/baeldung/Ledger.java b/redis/src/main/java/com/baeldung/Ledger.java new file mode 100644 index 000000000000..da72791f7ec8 --- /dev/null +++ b/redis/src/main/java/com/baeldung/Ledger.java @@ -0,0 +1,21 @@ +package com.baeldung; + +public class Ledger { + + public Ledger() { + } + + public Ledger(String name) { + this.name = name; + } + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/redis/src/main/java/com/baeldung/LedgerLiveObject.java b/redis/src/main/java/com/baeldung/LedgerLiveObject.java new file mode 100644 index 000000000000..395498968e10 --- /dev/null +++ b/redis/src/main/java/com/baeldung/LedgerLiveObject.java @@ -0,0 +1,21 @@ +package com.baeldung; + +import org.redisson.api.annotation.REntity; +import org.redisson.api.annotation.RId; + +/** + * Created by johnson on 3/9/17. + */ +@REntity +public class LedgerLiveObject { + @RId + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/redis/src/main/java/com/baeldung/LedgerServiceImpl.java b/redis/src/main/java/com/baeldung/LedgerServiceImpl.java new file mode 100644 index 000000000000..207efbcdef83 --- /dev/null +++ b/redis/src/main/java/com/baeldung/LedgerServiceImpl.java @@ -0,0 +1,17 @@ +package com.baeldung; + +import java.util.Arrays; +import java.util.List; + +/** + * Created by johnson on 3/9/17. + */ +public class LedgerServiceImpl implements LedgerServiceInterface { + + String[] returnArray = {"entry1","entry2","entry3"}; + + @Override + public List getEntries(int count) { + return Arrays.asList(returnArray); + } +} diff --git a/redis/src/main/java/com/baeldung/LedgerServiceInterface.java b/redis/src/main/java/com/baeldung/LedgerServiceInterface.java new file mode 100644 index 000000000000..918623ef7bd6 --- /dev/null +++ b/redis/src/main/java/com/baeldung/LedgerServiceInterface.java @@ -0,0 +1,10 @@ +package com.baeldung; + +import java.util.List; + +/** + * Created by johnson on 3/9/17. + */ +public interface LedgerServiceInterface { + List getEntries(int count); +} diff --git a/redis/src/main/resources/singleNodeConfig.json b/redis/src/main/resources/singleNodeConfig.json new file mode 100644 index 000000000000..f56e13dcfca7 --- /dev/null +++ b/redis/src/main/resources/singleNodeConfig.json @@ -0,0 +1,27 @@ +{ + "singleServerConfig": { + "idleConnectionTimeout": 10000, + "pingTimeout": 1000, + "connectTimeout": 10000, + "timeout": 3000, + "retryAttempts": 3, + "retryInterval": 1500, + "reconnectionTimeout": 3000, + "failedAttempts": 3, + "password": null, + "subscriptionsPerConnection": 5, + "clientName": null, + "address": "redis://127.0.0.1:6379", + "subscriptionConnectionMinimumIdleSize": 1, + "subscriptionConnectionPoolSize": 50, + "connectionMinimumIdleSize": 10, + "connectionPoolSize": 64, + "database": 0, + "dnsMonitoring": false, + "dnsMonitoringInterval": 5000 + }, + "threads": 0, + "nettyThreads": 0, + "codec": null, + "useLinuxNativeEpoll": false +} \ No newline at end of file diff --git a/redis/src/main/resources/singleNodeConfig.yaml b/redis/src/main/resources/singleNodeConfig.yaml new file mode 100644 index 000000000000..1b05c46be28a --- /dev/null +++ b/redis/src/main/resources/singleNodeConfig.yaml @@ -0,0 +1,24 @@ +singleServerConfig: + idleConnectionTimeout: 10000 + pingTimeout: 1000 + connectTimeout: 10000 + timeout: 3000 + retryAttempts: 3 + retryInterval: 1500 + reconnectionTimeout: 3000 + failedAttempts: 3 + password: null + subscriptionsPerConnection: 5 + clientName: null + address: "redis://127.0.0.1:6379" + subscriptionConnectionMinimumIdleSize: 1 + subscriptionConnectionPoolSize: 50 + connectionMinimumIdleSize: 10 + connectionPoolSize: 64 + database: 0 + dnsMonitoring: false + dnsMonitoringInterval: 5000 +threads: 0 +nettyThreads: 0 +codec: ! {} +useLinuxNativeEpoll: false \ No newline at end of file diff --git a/redis/src/test/java/com/baeldung/JedisTest.java b/redis/src/test/java/com/baeldung/JedisIntegrationTest.java similarity index 98% rename from redis/src/test/java/com/baeldung/JedisTest.java rename to redis/src/test/java/com/baeldung/JedisIntegrationTest.java index 582bb266aaaf..c1ec9bd2f812 100644 --- a/redis/src/test/java/com/baeldung/JedisTest.java +++ b/redis/src/test/java/com/baeldung/JedisIntegrationTest.java @@ -10,12 +10,12 @@ import java.util.Map; import java.util.Set; -public class JedisTest { +public class JedisIntegrationTest { private Jedis jedis; private static RedisServer redisServer; - public JedisTest() { + public JedisIntegrationTest() { jedis = new Jedis(); } diff --git a/redis/src/test/java/com/baeldung/RedissonConfigurationIntegrationTest.java b/redis/src/test/java/com/baeldung/RedissonConfigurationIntegrationTest.java new file mode 100644 index 000000000000..1862d6b035e4 --- /dev/null +++ b/redis/src/test/java/com/baeldung/RedissonConfigurationIntegrationTest.java @@ -0,0 +1,65 @@ +package com.baeldung; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.redisson.Redisson; +import org.redisson.api.RedissonClient; +import org.redisson.config.Config; +import redis.embedded.RedisServer; + +import java.io.File; +import java.io.IOException; + +/** + * Created by johnson on 3/9/17. + */ +public class RedissonConfigurationIntegrationTest { + private static RedisServer redisServer; + private static RedissonClient client; + + @BeforeClass + public static void setUp() throws IOException { + redisServer = new RedisServer(6379); + redisServer.start(); + } + + @AfterClass + public static void destroy() { + redisServer.stop(); + client.shutdown(); + } + + @Test + public void givenJavaConfig_thenRedissonConnectToRedis() { + Config config = new Config(); + config.useSingleServer() + .setAddress("127.0.0.1:6379"); + + client = Redisson.create(config); + + assert(client != null && client.getKeys().count() >= 0); + } + + @Test + public void givenJSONFileConfig_thenRedissonConnectToRedis() throws IOException { + Config config = Config.fromJSON( + new File(getClass().getClassLoader().getResource( + "singleNodeConfig.json").getFile())); + + client = Redisson.create(config); + + assert(client != null && client.getKeys().count() >= 0); + } + + @Test + public void givenYAMLFileConfig_thenRedissonConnectToRedis() throws IOException { + Config config = Config.fromYAML( + new File(getClass().getClassLoader().getResource( + "singleNodeConfig.yaml").getFile())); + + client = Redisson.create(config); + + assert(client != null && client.getKeys().count() >= 0); + } +} \ No newline at end of file diff --git a/redis/src/test/java/com/baeldung/RedissonIntegrationTest.java b/redis/src/test/java/com/baeldung/RedissonIntegrationTest.java new file mode 100644 index 000000000000..766963e5cf37 --- /dev/null +++ b/redis/src/test/java/com/baeldung/RedissonIntegrationTest.java @@ -0,0 +1,232 @@ +package com.baeldung; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.redisson.Redisson; +import org.redisson.RedissonMultiLock; +import org.redisson.api.*; +import org.redisson.client.RedisClient; +import org.redisson.client.RedisConnection; +import org.redisson.client.codec.StringCodec; +import org.redisson.client.protocol.RedisCommands; +import redis.embedded.RedisServer; + +import java.io.IOException; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class RedissonIntegrationTest { + + private static RedisServer redisServer; + private static RedissonClient client; + + @BeforeClass + public static void setUp() throws IOException { + redisServer = new RedisServer(6379); + redisServer.start(); + client = Redisson.create(); + } + + @AfterClass + public static void destroy() { + redisServer.stop(); + client.shutdown(); + } + + @Test + public void givenMultipleKeysInRedis_thenGetAllKeys() { + client.getBucket("key1").set("key1"); + client.getBucket("key2").set("key2"); + client.getBucket("key3").set("key3"); + + RKeys keys = client.getKeys(); + + assertTrue(keys.count() >= 3); + } + + @Test + public void givenKeysWithPatternInRedis_thenGetPatternKeys() { + client.getBucket("key1").set("key1"); + client.getBucket("key2").set("key2"); + client.getBucket("key3").set("key3"); + client.getBucket("id4").set("id4"); + + RKeys keys = client.getKeys(); + + Iterable keysWithPattern + = keys.getKeysByPattern("key*"); + + List keyWithPatternList + = StreamSupport.stream( + keysWithPattern.spliterator(), + false).collect(Collectors.toList()); + + assertTrue(keyWithPatternList.size() == 3); + } + + @Test + public void givenAnObject_thenSaveToRedis() { + RBucket bucket = client.getBucket("ledger"); + Ledger ledger = new Ledger(); + ledger.setName("ledger1"); + bucket.set(ledger); + + Ledger returnedLedger = bucket.get(); + + assertTrue( + returnedLedger != null + && returnedLedger.getName().equals("ledger1")); + } + + @Test + public void givenALong_thenSaveLongToRedisAndAtomicallyIncrement(){ + Long value = 5L; + + RAtomicLong atomicLong + = client.getAtomicLong("myAtomicLong"); + atomicLong.set(value); + Long returnValue = atomicLong.incrementAndGet(); + + assertTrue(returnValue == 6L); + } + + @Test + public void givenTopicSubscribedToAChannel_thenReceiveMessageFromChannel() throws ExecutionException, InterruptedException { + CompletableFuture future = new CompletableFuture<>(); + + RTopic subscribeTopic = client.getTopic("baeldung"); + subscribeTopic.addListener((channel, customMessage) -> future.complete(customMessage.getMessage())); + + RTopic publishTopic = client.getTopic("baeldung"); + long clientsReceivedMessage + = publishTopic.publish(new CustomMessage("This is a message")); + + assertEquals("This is a message", future.get()); + + } + + @Test + public void givenAMap_thenSaveMapToRedis(){ + RMap map = client.getMap("ledger"); + map.put("123", new Ledger("ledger")); + + assertTrue(map.get("123").getName().equals("ledger")); + } + + @Test + public void givenASet_thenSaveSetToRedis(){ + RSet ledgerSet = client.getSet("ledgerSet"); + ledgerSet.add(new Ledger("ledger")); + + assertTrue(ledgerSet.contains(new Ledger("ledger"))); + } + + @Test + public void givenAList_thenSaveListToRedis(){ + RList ledgerList = client.getList("ledgerList"); + ledgerList.add(new Ledger("ledger")); + + assertTrue(ledgerList.contains(new Ledger("ledger"))); + } + + @Test + public void givenLockSet_thenEnsureCanUnlock(){ + RLock lock = client.getLock("lock"); + lock.lock(); + assertTrue(lock.isLocked()); + + lock.unlock(); + assertTrue(!lock.isLocked()); + } + + @Test + public void givenMultipleLocksSet_thenEnsureAllCanUnlock(){ + RedissonClient clientInstance1 = Redisson.create(); + RedissonClient clientInstance2 = Redisson.create(); + RedissonClient clientInstance3 = Redisson.create(); + + RLock lock1 = clientInstance1.getLock("lock1"); + RLock lock2 = clientInstance2.getLock("lock2"); + RLock lock3 = clientInstance3.getLock("lock3"); + + RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3); + lock.lock(); + assertTrue(lock1.isLocked() && lock2.isLocked() && lock3.isLocked()); + + lock.unlock(); + assertTrue(!(lock1.isLocked() || lock2.isLocked() || lock3.isLocked())); + } + + @Test + public void givenRemoteServiceMethodRegistered_thenInvokeMethod(){ + RRemoteService remoteService = client.getRemoteService(); + LedgerServiceImpl ledgerServiceImpl = new LedgerServiceImpl(); + + remoteService.register(LedgerServiceInterface.class, ledgerServiceImpl); + + + LedgerServiceInterface ledgerService + = remoteService.get(LedgerServiceInterface.class); + + List entries = ledgerService.getEntries(10); + + assertTrue(entries.size() == 3 && entries.contains("entry1")); + } + + @Test + public void givenLiveObjectPersisted_thenGetLiveObject(){ + RLiveObjectService service = client.getLiveObjectService(); + + LedgerLiveObject ledger = new LedgerLiveObject(); + ledger.setName("ledger1"); + + ledger = service.persist(ledger); + + LedgerLiveObject returnLedger + = service.get(LedgerLiveObject.class, "ledger1"); + + assertTrue(ledger.getName().equals(returnLedger.getName())); + } + + @Test + public void givenMultipleOperations_thenDoAllAtomically(){ + RBatch batch = client.createBatch(); + batch.getMap("ledgerMap").fastPutAsync("1", "2"); + batch.getMap("ledgerMap").putAsync("2", "5"); + + List result = batch.execute(); + + RMap map = client.getMap("ledgerMap"); + assertTrue(result.size() > 0 && map.get("1").equals("2")); + } + + @Test + public void givenLUAScript_thenExecuteScriptOnRedis(){ + client.getBucket("foo").set("bar"); + String result = client.getScript().eval(RScript.Mode.READ_ONLY, + "return redis.call('get', 'foo')", RScript.ReturnType.VALUE); + + assertTrue(result.equals("bar")); + } + + @Test + public void givenLowLevelRedisCommands_thenExecuteLowLevelCommandsOnRedis(){ + RedisClient client = new RedisClient("localhost", 6379); + RedisConnection conn = client.connect(); + conn.sync(StringCodec.INSTANCE, RedisCommands.SET, "test", 0); + + String testValue = conn.sync(StringCodec.INSTANCE, RedisCommands.GET, "test"); + + conn.closeAsync(); + client.shutdown(); + + assertTrue(testValue.equals("0")); + } +} diff --git a/rest-assured/pom.xml b/rest-assured/pom.xml index bbeef8bb9cfd..0c3bf38dc566 100644 --- a/rest-assured/pom.xml +++ b/rest-assured/pom.xml @@ -60,20 +60,15 @@ - log4j - log4j - ${log4j.version} - - - org.slf4j - slf4j-log4j12 - ${slf4j.version} + ch.qos.logback + logback-classic + ${logback.version} - commons-logging - commons-logging - ${commons-logging.version} + ch.qos.logback + logback-core + ${logback.version} @@ -150,6 +145,12 @@ org.apache.httpcomponents httpclient ${httpclient.version} + + + commons-logging + commons-logging + + @@ -183,6 +184,12 @@ com.github.fge json-schema-core ${json-schema-core.version} + + + commons-logging + commons-logging + + junit @@ -276,11 +283,11 @@ 1.4.7 9.4.0.v20161208 - 1.2.17 1.7.21 + 1.1.7 + 3.5 - 1.2 3.2.2 4.4.5 diff --git a/rest-assured/src/test/resources/log4j.properties b/rest-assured/src/test/resources/log4j.properties deleted file mode 100644 index d3c6b9e78394..000000000000 --- a/rest-assured/src/test/resources/log4j.properties +++ /dev/null @@ -1,16 +0,0 @@ -## Logger configure -datestamp=yyyy-MM-dd HH:mm:ss -log4j.rootLogger=TRACE, file, console - -log4j.appender.file=org.apache.log4j.RollingFileAppender -log4j.appender.file.maxFileSize=1GB -log4j.appender.file.maxBackupIndex=5 -log4j.appender.file.File=log/rest-assured.log -log4j.appender.file.threshold=TRACE -log4j.appender.file.layout=org.apache.log4j.PatternLayout -log4j.appender.file.layout.ConversionPattern=%d{${datestamp}} %5p: [%c] - %m%n - -log4j.appender.console=org.apache.log4j.ConsoleAppender -log4j.appender.console.Threshold=INFO -log4j.appender.console.layout=org.apache.log4j.PatternLayout -log4j.appender.console.layout.ConversionPattern=%d{${datestamp}} %5p\: [%c] - %m%n \ No newline at end of file diff --git a/rest-assured/src/test/resources/logback.xml b/rest-assured/src/test/resources/logback.xml new file mode 100644 index 000000000000..ec0dc2469ae0 --- /dev/null +++ b/rest-assured/src/test/resources/logback.xml @@ -0,0 +1,19 @@ + + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + + + + + + + + + + + + \ No newline at end of file diff --git a/rest-testing/README.md b/rest-testing/README.md index 5dd4e4916224..37a53dd81516 100644 --- a/rest-testing/README.md +++ b/rest-testing/README.md @@ -9,3 +9,4 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring - [Test a REST API with Java](http://www.baeldung.com/2011/10/13/integration-testing-a-rest-api/) - [Introduction to WireMock](http://www.baeldung.com/introduction-to-wiremock) - [REST API Testing with Cucumber](http://www.baeldung.com/cucumber-rest-api-testing) +- [Testing a REST API with JBehave](http://www.baeldung.com/jbehave-rest-testing) diff --git a/rest-testing/pom.xml b/rest-testing/pom.xml index f7f94a49a6e5..a6a9bb39b3a9 100644 --- a/rest-testing/pom.xml +++ b/rest-testing/pom.xml @@ -1,164 +1,172 @@ - 4.0.0 - com.baeldung - rest-testing - 0.1-SNAPSHOT - - rest-testing - - - - - - - com.google.guava - guava - ${guava.version} - - - - commons-io - commons-io - ${commons-io.version} - - - - org.apache.commons - commons-lang3 - ${commons-lang3.version} - - - - - - org.apache.httpcomponents - httpclient - ${httpclient.version} - - - org.apache.httpcomponents - httpcore - ${httpcore.version} - - - - - - com.fasterxml.jackson.core - jackson-databind - ${jackson.version} - - - - - - org.slf4j - slf4j-api - ${org.slf4j.version} - - - ch.qos.logback - logback-classic - ${logback.version} - - - - org.slf4j - jcl-over-slf4j - ${org.slf4j.version} - runtime - - - org.slf4j - log4j-over-slf4j - ${org.slf4j.version} - - - - - - junit - junit - ${junit.version} - test - - - - org.hamcrest - hamcrest-core - ${org.hamcrest.version} - test - - - org.hamcrest - hamcrest-library - ${org.hamcrest.version} - test - - - - org.mockito - mockito-core - ${mockito.version} - test - - - com.github.tomakehurst - wiremock - ${wiremock.version} - test - - - - info.cukes - cucumber-java - ${cucumber.version} - test - - - info.cukes - cucumber-junit - ${cucumber.version} - - - - - rest-testing - - - src/main/resources - true - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - ${maven-compiler-plugin.version} - - 1.8 - 1.8 - - - - - org.apache.maven.plugins - maven-surefire-plugin - ${maven-surefire-plugin.version} - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + com.baeldung + rest-testing + 0.1-SNAPSHOT + + rest-testing + + + + + + + com.google.guava + guava + ${guava.version} + + + + commons-io + commons-io + ${commons-io.version} + + + + org.apache.commons + commons-lang3 + ${commons-lang3.version} + + + + + + org.apache.httpcomponents + httpclient + ${httpclient.version} + + + commons-logging + commons-logging + + + + + org.apache.httpcomponents + httpcore + ${httpcore.version} + + + + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + + + + + + org.slf4j + slf4j-api + ${org.slf4j.version} + + + ch.qos.logback + logback-classic + ${logback.version} + + + + org.slf4j + jcl-over-slf4j + ${org.slf4j.version} + runtime + + + + + + junit + junit + ${junit.version} + test + + + + org.hamcrest + hamcrest-core + ${org.hamcrest.version} + test + + + org.hamcrest + hamcrest-library + ${org.hamcrest.version} + test + + + + org.mockito + mockito-core + ${mockito.version} + test + + + com.github.tomakehurst + wiremock + ${wiremock.version} + test + + + + info.cukes + cucumber-java + ${cucumber.version} + test + + + info.cukes + cucumber-junit + ${cucumber.version} + + + + org.jbehave + jbehave-core + ${jbehave.version} + test + + + + + rest-testing + + + src/main/resources + true + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + 1.8 + 1.8 + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + **/*IntegrationTest.java **/*LiveTest.java - + - + - + @@ -177,10 +185,10 @@ **/*UnitTest.java + **/*LiveTest.java **/*IntegrationTest.java - **/*LiveTest.java @@ -195,37 +203,38 @@ - - - - 2.8.5 - - - 1.7.21 - 1.1.7 - - - 19.0 - 3.5 - 2.5 - - - 1.3 - 4.12 - 1.10.19 + + + + 2.8.5 + + + 1.7.21 + 1.1.7 + + + 19.0 + 3.5 + 2.5 + + + 1.3 + 4.12 + 1.10.19 2.9.0 1.2.5 2.4.1 - 4.4.5 - 4.5.2 + 4.4.5 + 4.5.2 + 4.1 - - 3.6.0 - 2.6 - 2.19.1 + + 3.6.0 + 2.6 + 2.19.1 - + \ No newline at end of file diff --git a/rest-testing/src/main/resources/logback.xml b/rest-testing/src/main/resources/logback.xml index aa1e9cd52209..ec0dc2469ae0 100644 --- a/rest-testing/src/main/resources/logback.xml +++ b/rest-testing/src/main/resources/logback.xml @@ -1,5 +1,5 @@ + - web - %date [%thread] %-5level %logger{36} - %message%n @@ -7,11 +7,13 @@ - - + + + + + - \ No newline at end of file diff --git a/rest-testing/src/test/java/com/baeldung/rest/jbehave/AbstractStory.java b/rest-testing/src/test/java/com/baeldung/rest/jbehave/AbstractStory.java new file mode 100644 index 000000000000..ce3c98d07eba --- /dev/null +++ b/rest-testing/src/test/java/com/baeldung/rest/jbehave/AbstractStory.java @@ -0,0 +1,45 @@ +package com.baeldung.rest.jbehave; + +import org.jbehave.core.configuration.Configuration; +import org.jbehave.core.configuration.MostUsefulConfiguration; +import org.jbehave.core.io.LoadFromClasspath; +import org.jbehave.core.junit.JUnitStories; +import org.jbehave.core.reporters.StoryReporterBuilder; +import org.jbehave.core.steps.InjectableStepsFactory; +import org.jbehave.core.steps.InstanceStepsFactory; + +import java.util.Arrays; +import java.util.List; + +import static org.jbehave.core.io.CodeLocations.codeLocationFromClass; +import static org.jbehave.core.reporters.Format.CONSOLE; + +/** + * @author aiet + */ +public abstract class AbstractStory extends JUnitStories { + + abstract String storyName(); + + @Override + public Configuration configuration() { + return new MostUsefulConfiguration() + .useStoryLoader(new LoadFromClasspath(this.getClass())) + .useStoryReporterBuilder(new StoryReporterBuilder() + .withCodeLocation(codeLocationFromClass(this.getClass())) + .withFormats(CONSOLE)); + } + + abstract Object stepInstance(); + + @Override + public InjectableStepsFactory stepsFactory() { + return new InstanceStepsFactory(configuration(), stepInstance()); + } + + @Override + protected List storyPaths() { + return Arrays.asList(storyName()); + } + +} diff --git a/rest-testing/src/test/java/com/baeldung/rest/jbehave/GithubUserNotFoundSteps.java b/rest-testing/src/test/java/com/baeldung/rest/jbehave/GithubUserNotFoundSteps.java new file mode 100644 index 000000000000..54d3b0b155ab --- /dev/null +++ b/rest-testing/src/test/java/com/baeldung/rest/jbehave/GithubUserNotFoundSteps.java @@ -0,0 +1,59 @@ +package com.baeldung.rest.jbehave; + +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.impl.client.HttpClientBuilder; +import org.jbehave.core.annotations.Given; +import org.jbehave.core.annotations.Then; +import org.jbehave.core.annotations.When; + +import java.io.IOException; + +import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic; +import static org.apache.http.HttpStatus.SC_NOT_FOUND; +import static org.junit.Assert.assertTrue; + +public class GithubUserNotFoundSteps { + + private String api; + private String nonExistentUser; + private int githubResponseCode; + + @Given("github user profile api") + public void givenGithubUserProfileApi() { + api = "https://api.github.com/users/%s"; + } + + @Given("a random non-existent username") + public void givenANonexistentUsername() { + nonExistentUser = randomAlphabetic(8); + } + + @When("I look for the random user via the api") + public void whenILookForTheUserViaTheApi() throws IOException { + githubResponseCode = getGithubUserProfile(api, nonExistentUser) + .getStatusLine() + .getStatusCode(); + } + + @When("I look for $user via the api") + public void whenILookForSomeNonExistentUserViaTheApi(String user) throws IOException { + githubResponseCode = getGithubUserProfile(api, user) + .getStatusLine() + .getStatusCode(); + } + + static HttpResponse getGithubUserProfile(String api, String username) throws IOException { + HttpUriRequest request = new HttpGet(String.format(api, username)); + return HttpClientBuilder + .create() + .build() + .execute(request); + } + + @Then("github respond: 404 not found") + public void thenGithubRespond404NotFound() { + assertTrue(SC_NOT_FOUND == githubResponseCode); + } +} diff --git a/rest-testing/src/test/java/com/baeldung/rest/jbehave/GithubUserNotFoundStoryLiveTest.java b/rest-testing/src/test/java/com/baeldung/rest/jbehave/GithubUserNotFoundStoryLiveTest.java new file mode 100644 index 000000000000..f681fb085a2e --- /dev/null +++ b/rest-testing/src/test/java/com/baeldung/rest/jbehave/GithubUserNotFoundStoryLiveTest.java @@ -0,0 +1,18 @@ +package com.baeldung.rest.jbehave; + +/** + * @author aiet + */ +public class GithubUserNotFoundStoryLiveTest extends AbstractStory { + + @Override + String storyName() { + return "github_user_not_found.story"; + } + + @Override + Object stepInstance() { + return new GithubUserNotFoundSteps(); + } + +} diff --git a/rest-testing/src/test/java/com/baeldung/rest/jbehave/GithubUserResponseMediaTypeSteps.java b/rest-testing/src/test/java/com/baeldung/rest/jbehave/GithubUserResponseMediaTypeSteps.java new file mode 100644 index 000000000000..a1a9aceee5b2 --- /dev/null +++ b/rest-testing/src/test/java/com/baeldung/rest/jbehave/GithubUserResponseMediaTypeSteps.java @@ -0,0 +1,40 @@ +package com.baeldung.rest.jbehave; + +import org.apache.http.entity.ContentType; +import org.jbehave.core.annotations.Given; +import org.jbehave.core.annotations.Then; +import org.jbehave.core.annotations.When; + +import java.io.IOException; + +import static com.baeldung.rest.jbehave.GithubUserNotFoundSteps.getGithubUserProfile; +import static org.junit.Assert.assertEquals; + +public class GithubUserResponseMediaTypeSteps { + + private String api; + private String validUser; + private String mediaType; + + @Given("github user profile api") + public void givenGithubUserProfileApi() { + api = "https://api.github.com/users/%s"; + } + + @Given("a valid username") + public void givenAValidUsername() { + validUser = "eugenp"; + } + + @When("I look for the user via the api") + public void whenILookForTheUserViaTheApi() throws IOException { + mediaType = ContentType + .getOrDefault(getGithubUserProfile(api, validUser).getEntity()) + .getMimeType(); + } + + @Then("github respond data of type json") + public void thenGithubRespondDataOfTypeJson() { + assertEquals("application/json", mediaType); + } +} diff --git a/rest-testing/src/test/java/com/baeldung/rest/jbehave/GithubUserResponseMediaTypeStoryLiveTest.java b/rest-testing/src/test/java/com/baeldung/rest/jbehave/GithubUserResponseMediaTypeStoryLiveTest.java new file mode 100644 index 000000000000..c14f7aad75db --- /dev/null +++ b/rest-testing/src/test/java/com/baeldung/rest/jbehave/GithubUserResponseMediaTypeStoryLiveTest.java @@ -0,0 +1,18 @@ +package com.baeldung.rest.jbehave; + +/** + * @author aiet + */ +public class GithubUserResponseMediaTypeStoryLiveTest extends AbstractStory { + + @Override + String storyName() { + return "github_user_response_mediatype.story"; + } + + @Override + Object stepInstance() { + return new GithubUserResponseMediaTypeSteps(); + } + +} diff --git a/rest-testing/src/test/java/com/baeldung/rest/jbehave/GithubUserResponsePayloadSteps.java b/rest-testing/src/test/java/com/baeldung/rest/jbehave/GithubUserResponsePayloadSteps.java new file mode 100644 index 000000000000..d9401a5d0079 --- /dev/null +++ b/rest-testing/src/test/java/com/baeldung/rest/jbehave/GithubUserResponsePayloadSteps.java @@ -0,0 +1,40 @@ +package com.baeldung.rest.jbehave; + +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.impl.client.HttpClientBuilder; +import org.baeldung.rest.GitHubUser; +import org.baeldung.rest.RetrieveUtil; +import org.hamcrest.Matchers; +import org.jbehave.core.annotations.Given; +import org.jbehave.core.annotations.Then; +import org.jbehave.core.annotations.When; + +import java.io.IOException; + +import static com.baeldung.rest.jbehave.GithubUserNotFoundSteps.getGithubUserProfile; +import static org.hamcrest.MatcherAssert.assertThat; + +public class GithubUserResponsePayloadSteps { + + private String api; + private GitHubUser resource; + + @Given("github user profile api") + public void givenGithubUserProfileApi() { + api = "https://api.github.com/users/%s"; + } + + @When("I look for $user via the api") + public void whenILookForEugenpViaTheApi(String user) throws IOException { + HttpResponse httpResponse = getGithubUserProfile(api, user); + resource = RetrieveUtil.retrieveResourceFromResponse(httpResponse, GitHubUser.class); + } + + @Then("github's response contains a 'login' payload same as $username") + public void thenGithubsResponseContainsAloginPayloadSameAsEugenp(String username) { + assertThat(username, Matchers.is(resource.getLogin())); + } + +} diff --git a/rest-testing/src/test/java/com/baeldung/rest/jbehave/GithubUserResponsePayloadStoryLiveTest.java b/rest-testing/src/test/java/com/baeldung/rest/jbehave/GithubUserResponsePayloadStoryLiveTest.java new file mode 100644 index 000000000000..cd7974869a89 --- /dev/null +++ b/rest-testing/src/test/java/com/baeldung/rest/jbehave/GithubUserResponsePayloadStoryLiveTest.java @@ -0,0 +1,18 @@ +package com.baeldung.rest.jbehave; + +/** + * @author aiet + */ +public class GithubUserResponsePayloadStoryLiveTest extends AbstractStory { + + @Override + String storyName() { + return "github_user_response_payload.story"; + } + + @Override + Object stepInstance() { + return new GithubUserResponsePayloadSteps(); + } + +} diff --git a/rest-testing/src/test/java/com/baeldung/rest/jbehave/IncreaseSteps.java b/rest-testing/src/test/java/com/baeldung/rest/jbehave/IncreaseSteps.java new file mode 100644 index 000000000000..3bc6fb2aa23d --- /dev/null +++ b/rest-testing/src/test/java/com/baeldung/rest/jbehave/IncreaseSteps.java @@ -0,0 +1,34 @@ +package com.baeldung.rest.jbehave; + +import org.jbehave.core.annotations.Given; +import org.jbehave.core.annotations.Then; +import org.jbehave.core.annotations.When; + +import java.util.Random; + +import static org.junit.Assert.assertTrue; + +public class IncreaseSteps { + private int counter; + private int previousValue; + + @Given("a counter") + public void aCounter() { + } + + @Given("the counter has any integral value") + public void counterHasAnyIntegralValue() { + counter = new Random().nextInt(); + previousValue = counter; + } + + @When("the user increases the counter") + public void increasesTheCounter() { + counter++; + } + + @Then("the value of the counter must be 1 greater than previous value") + public void theValueOfTheCounterMustBe1Greater() { + assertTrue(1 == counter - previousValue); + } +} diff --git a/rest-testing/src/test/java/com/baeldung/rest/jbehave/IncreaseStoryLiveTest.java b/rest-testing/src/test/java/com/baeldung/rest/jbehave/IncreaseStoryLiveTest.java new file mode 100644 index 000000000000..9033dbfbdca2 --- /dev/null +++ b/rest-testing/src/test/java/com/baeldung/rest/jbehave/IncreaseStoryLiveTest.java @@ -0,0 +1,38 @@ +package com.baeldung.rest.jbehave; + +import org.jbehave.core.configuration.Configuration; +import org.jbehave.core.configuration.MostUsefulConfiguration; +import org.jbehave.core.io.LoadFromClasspath; +import org.jbehave.core.junit.JUnitStories; +import org.jbehave.core.reporters.StoryReporterBuilder; +import org.jbehave.core.steps.InjectableStepsFactory; +import org.jbehave.core.steps.InstanceStepsFactory; + +import java.util.Arrays; +import java.util.List; + +import static org.jbehave.core.io.CodeLocations.codeLocationFromClass; +import static org.jbehave.core.reporters.Format.CONSOLE; + +public class IncreaseStoryLiveTest extends JUnitStories { + + @Override + public Configuration configuration() { + return new MostUsefulConfiguration() + .useStoryLoader(new LoadFromClasspath(this.getClass())) + .useStoryReporterBuilder(new StoryReporterBuilder() + .withCodeLocation(codeLocationFromClass(this.getClass())) + .withFormats(CONSOLE)); + } + + @Override + public InjectableStepsFactory stepsFactory() { + return new InstanceStepsFactory(configuration(), new IncreaseSteps()); + } + + @Override + protected List storyPaths() { + return Arrays.asList("increase.story"); + } + +} diff --git a/rest-testing/src/test/resources/github_user_not_found.story b/rest-testing/src/test/resources/github_user_not_found.story new file mode 100644 index 000000000000..1ab6920eee99 --- /dev/null +++ b/rest-testing/src/test/resources/github_user_not_found.story @@ -0,0 +1,19 @@ +Meta: + +Narrative: +As a user +I want to look up a non-existent user's profile on github +So that I can be sure that the username can not be found on github + +Scenario: when a user checks a non-existent user on github, github would respond 'not found' + +Given github user profile api +And a random non-existent username +When I look for the random user via the api +Then github respond: 404 not found + +When I look for eugenp1 via the api +Then github respond: 404 not found + +When I look for eugenp2 via the api +Then github respond: 404 not found diff --git a/rest-testing/src/test/resources/github_user_response_mediatype.story b/rest-testing/src/test/resources/github_user_response_mediatype.story new file mode 100644 index 000000000000..7c4a0b9e2dba --- /dev/null +++ b/rest-testing/src/test/resources/github_user_response_mediatype.story @@ -0,0 +1,13 @@ +Meta: + +Narrative: +As a user +I want to look up a valid user's profile on github +So that I can know that github responds data of type json + +Scenario: when a user checks a valid user's profile on github, github would respond json data + +Given github user profile api +And a valid username +When I look for the user via the api +Then github respond data of type json \ No newline at end of file diff --git a/rest-testing/src/test/resources/github_user_response_payload.story b/rest-testing/src/test/resources/github_user_response_payload.story new file mode 100644 index 000000000000..788b43eba6aa --- /dev/null +++ b/rest-testing/src/test/resources/github_user_response_payload.story @@ -0,0 +1,12 @@ +Meta: + +Narrative: +As a user +I want to look up a valid user's profile on github +So that I can know the login payload should be the same as username + +Scenario: when a user checks a valid user's profile on github, github's response json should include a login payload with the same username + +Given github user profile api +When I look for eugenp via the api +Then github's response contains a 'login' payload same as eugenp diff --git a/rest-testing/src/test/resources/increase.story b/rest-testing/src/test/resources/increase.story new file mode 100644 index 000000000000..fef91298bcbb --- /dev/null +++ b/rest-testing/src/test/resources/increase.story @@ -0,0 +1,15 @@ +JBehave Story - An increase test + +Meta: + +Narrative: +As a user +I want to increase a counter +So that I can have the counter's value increase by 1 + +Scenario: when a user increases a counter, its value is increased by 1 + +Given a counter +And the counter has any integral value +When the user increases the counter +Then the value of the counter must be 1 greater than previous value \ No newline at end of file diff --git a/resteasy/README.md b/resteasy/README.md index 722f1dfe933e..83051037d2ad 100644 --- a/resteasy/README.md +++ b/resteasy/README.md @@ -6,3 +6,4 @@ ### Relevant Articles: - [A Guide to RESTEasy](http://www.baeldung.com/resteasy-tutorial) - [RESTEasy Client API](http://www.baeldung.com/resteasy-client-tutorial) +- [CORS in JAX-RS](http://www.baeldung.com/cors-in-jax-rs) diff --git a/resteasy/bin/README.md b/resteasy/bin/README.md new file mode 100644 index 000000000000..722f1dfe933e --- /dev/null +++ b/resteasy/bin/README.md @@ -0,0 +1,8 @@ +========= + +## A Guide to RESTEasy + + +### Relevant Articles: +- [A Guide to RESTEasy](http://www.baeldung.com/resteasy-tutorial) +- [RESTEasy Client API](http://www.baeldung.com/resteasy-client-tutorial) diff --git a/resteasy/bin/pom.xml b/resteasy/bin/pom.xml new file mode 100644 index 000000000000..1116c0f01ada --- /dev/null +++ b/resteasy/bin/pom.xml @@ -0,0 +1,173 @@ + + + 4.0.0 + + com.baeldung + resteasy-tutorial + 1.0 + war + + + 3.0.19.Final + 4.12 + 2.5 + 2.19.1 + 1.6.1 + + + + RestEasyTutorial + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + 3 + true + + **/*IntegrationTest.java + **/*LiveTest.java + + + + + + org.codehaus.cargo + cargo-maven2-plugin + ${cargo-maven2-plugin.version} + + true + + jetty8x + embedded + + + + 8082 + + + + + + + + + + + + + org.jboss.resteasy + resteasy-servlet-initializer + ${resteasy.version} + + + + + org.jboss.resteasy + resteasy-client + ${resteasy.version} + + + + + + org.jboss.resteasy + resteasy-jaxb-provider + ${resteasy.version} + + + + org.jboss.resteasy + resteasy-jackson-provider + ${resteasy.version} + + + + + + junit + junit + ${junit.version} + + + + commons-io + commons-io + ${commons-io.version} + + + + + + + live + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + integration-test + + test + + + + **/*IntegrationTest.java + + + **/*LiveTest.java + + + + + + + json + + + + + org.codehaus.cargo + cargo-maven2-plugin + ${cargo-maven2-plugin.version} + + false + + + + start-server + pre-integration-test + + start + + + + stop-server + post-integration-test + + stop + + + + + + + + + + + \ No newline at end of file diff --git a/resteasy/bin/src/main/webapp/WEB-INF/classes/logback.xml b/resteasy/bin/src/main/webapp/WEB-INF/classes/logback.xml new file mode 100644 index 000000000000..ec0dc2469ae0 --- /dev/null +++ b/resteasy/bin/src/main/webapp/WEB-INF/classes/logback.xml @@ -0,0 +1,19 @@ + + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resteasy/bin/src/main/webapp/WEB-INF/jboss-deployment-structure.xml b/resteasy/bin/src/main/webapp/WEB-INF/jboss-deployment-structure.xml new file mode 100644 index 000000000000..cb258374a104 --- /dev/null +++ b/resteasy/bin/src/main/webapp/WEB-INF/jboss-deployment-structure.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/resteasy/bin/src/main/webapp/WEB-INF/jboss-web.xml b/resteasy/bin/src/main/webapp/WEB-INF/jboss-web.xml new file mode 100644 index 000000000000..694bb7133232 --- /dev/null +++ b/resteasy/bin/src/main/webapp/WEB-INF/jboss-web.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/resteasy/bin/src/main/webapp/WEB-INF/web.xml b/resteasy/bin/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000000..d5f00293f4e7 --- /dev/null +++ b/resteasy/bin/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,13 @@ + + + + RestEasy Example + + + resteasy.servlet.mapping.prefix + /rest + + + \ No newline at end of file diff --git a/resteasy/bin/src/main/webapp/script.js b/resteasy/bin/src/main/webapp/script.js new file mode 100644 index 000000000000..88198887b053 --- /dev/null +++ b/resteasy/bin/src/main/webapp/script.js @@ -0,0 +1,16 @@ +function call(url, type, data) { + var request = $.ajax({ + url : url, + method : "GET", + data : (data) ? JSON.stringify(data) : "", + dataType : type + }); + + request.done(function(resp) { + console.log(resp); + }); + + request.fail(function(jqXHR, textStatus) { + console.log("Request failed: " + textStatus); + }); +}; \ No newline at end of file diff --git a/resteasy/bin/src/test/resources/com/baeldung/server/movies/batman.json b/resteasy/bin/src/test/resources/com/baeldung/server/movies/batman.json new file mode 100644 index 000000000000..82aaaa8f40ba --- /dev/null +++ b/resteasy/bin/src/test/resources/com/baeldung/server/movies/batman.json @@ -0,0 +1,4 @@ +{ + "title": "Batman", + "imdbId": "tt0096895" +} \ No newline at end of file diff --git a/resteasy/bin/src/test/resources/com/baeldung/server/movies/transformer.json b/resteasy/bin/src/test/resources/com/baeldung/server/movies/transformer.json new file mode 100644 index 000000000000..634cefc73c99 --- /dev/null +++ b/resteasy/bin/src/test/resources/com/baeldung/server/movies/transformer.json @@ -0,0 +1,4 @@ +{ + "title": "Transformers", + "imdbId": "tt0418279" +} \ No newline at end of file diff --git a/resteasy/pom.xml b/resteasy/pom.xml index f0bd8298f507..eacde9235309 100644 --- a/resteasy/pom.xml +++ b/resteasy/pom.xml @@ -67,6 +67,12 @@ org.jboss.resteasy resteasy-servlet-initializer ${resteasy.version} + + + commons-logging + commons-logging + + diff --git a/resteasy/src/main/java/com/baeldung/filter/CORSFilter.java b/resteasy/src/main/java/com/baeldung/filter/CORSFilter.java new file mode 100644 index 000000000000..16562d435969 --- /dev/null +++ b/resteasy/src/main/java/com/baeldung/filter/CORSFilter.java @@ -0,0 +1,18 @@ +package com.baeldung.filter; + +import java.io.IOException; + +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerResponseContext; +import javax.ws.rs.container.ContainerResponseFilter; + +public class CORSFilter implements ContainerResponseFilter { + + @Override + public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) + throws IOException { + responseContext.getHeaders().add("Access-Control-Allow-Origin", "*"); + + } + +} diff --git a/resteasy/src/main/java/com/baeldung/server/MovieCrudService.java b/resteasy/src/main/java/com/baeldung/server/MovieCrudService.java index b7f3215f3ff2..5a623a2dc6df 100644 --- a/resteasy/src/main/java/com/baeldung/server/MovieCrudService.java +++ b/resteasy/src/main/java/com/baeldung/server/MovieCrudService.java @@ -13,71 +13,83 @@ @Path("/movies") public class MovieCrudService { - private Map inventory = new HashMap(); - - @GET - @Path("/getinfo") - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Movie movieByImdbId(@QueryParam("imdbId") String imdbId) { - - System.out.println("*** Calling getinfo for a given ImdbID***"); - - if (inventory.containsKey(imdbId)) { - return inventory.get(imdbId); - } else - return null; - } - - @POST - @Path("/addmovie") - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response addMovie(Movie movie) { - - System.out.println("*** Calling addMovie ***"); - - if (null != inventory.get(movie.getImdbId())) { - return Response.status(Response.Status.NOT_MODIFIED).entity("Movie is Already in the database.").build(); - } - - inventory.put(movie.getImdbId(), movie); - return Response.status(Response.Status.CREATED).build(); - } - - @PUT - @Path("/updatemovie") - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response updateMovie(Movie movie) { - - System.out.println("*** Calling updateMovie ***"); - - if (null == inventory.get(movie.getImdbId())) { - return Response.status(Response.Status.NOT_MODIFIED).entity("Movie is not in the database.\nUnable to Update").build(); - } - - inventory.put(movie.getImdbId(), movie); - return Response.status(Response.Status.OK).build(); - } - - @DELETE - @Path("/deletemovie") - public Response deleteMovie(@QueryParam("imdbId") String imdbId) { - - System.out.println("*** Calling deleteMovie ***"); - - if (null == inventory.get(imdbId)) { - return Response.status(Response.Status.NOT_FOUND).entity("Movie is not in the database.\nUnable to Delete").build(); - } - - inventory.remove(imdbId); - return Response.status(Response.Status.OK).build(); - } - - @GET - @Path("/listmovies") - @Produces({ "application/json" }) - public List listMovies() { - - return inventory.values().stream().collect(Collectors.toCollection(ArrayList::new)); - } + private Map inventory = new HashMap(); + + @GET + @Path("/") + @Produces({ MediaType.TEXT_PLAIN }) + public Response index() { + return Response.status(200).header("Access-Control-Allow-Origin", "*") + .header("Access-Control-Allow-Headers", "origin, content-type, accept, authorization") + .header("Access-Control-Allow-Credentials", "true") + .header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD").entity("").build(); + } + + @GET + @Path("/getinfo") + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + public Movie movieByImdbId(@QueryParam("imdbId") String imdbId) { + + System.out.println("*** Calling getinfo for a given ImdbID***"); + + if (inventory.containsKey(imdbId)) { + return inventory.get(imdbId); + } else + return null; + } + + @POST + @Path("/addmovie") + @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + public Response addMovie(Movie movie) { + + System.out.println("*** Calling addMovie ***"); + + if (null != inventory.get(movie.getImdbId())) { + return Response.status(Response.Status.NOT_MODIFIED).entity("Movie is Already in the database.").build(); + } + + inventory.put(movie.getImdbId(), movie); + return Response.status(Response.Status.CREATED).build(); + } + + @PUT + @Path("/updatemovie") + @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + public Response updateMovie(Movie movie) { + + System.out.println("*** Calling updateMovie ***"); + + if (null == inventory.get(movie.getImdbId())) { + return Response.status(Response.Status.NOT_MODIFIED) + .entity("Movie is not in the database.\nUnable to Update").build(); + } + + inventory.put(movie.getImdbId(), movie); + return Response.status(Response.Status.OK).build(); + } + + @DELETE + @Path("/deletemovie") + public Response deleteMovie(@QueryParam("imdbId") String imdbId) { + + System.out.println("*** Calling deleteMovie ***"); + + if (null == inventory.get(imdbId)) { + return Response.status(Response.Status.NOT_FOUND).entity("Movie is not in the database.\nUnable to Delete") + .build(); + } + + inventory.remove(imdbId); + return Response.status(Response.Status.OK).build(); + } + + @GET + @Path("/listmovies") + @Produces({ "application/json" }) + public List listMovies() { + + return inventory.values().stream().collect(Collectors.toCollection(ArrayList::new)); + } } diff --git a/resteasy/src/main/webapp/WEB-INF/classes/logback.xml b/resteasy/src/main/webapp/WEB-INF/classes/logback.xml index d94e9f71abbb..ec0dc2469ae0 100644 --- a/resteasy/src/main/webapp/WEB-INF/classes/logback.xml +++ b/resteasy/src/main/webapp/WEB-INF/classes/logback.xml @@ -1,3 +1,19 @@ - + + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + + + + + + + + + + \ No newline at end of file diff --git a/resteasy/src/main/webapp/script.js b/resteasy/src/main/webapp/script.js new file mode 100644 index 000000000000..88198887b053 --- /dev/null +++ b/resteasy/src/main/webapp/script.js @@ -0,0 +1,16 @@ +function call(url, type, data) { + var request = $.ajax({ + url : url, + method : "GET", + data : (data) ? JSON.stringify(data) : "", + dataType : type + }); + + request.done(function(resp) { + console.log(resp); + }); + + request.fail(function(jqXHR, textStatus) { + console.log("Request failed: " + textStatus); + }); +}; \ No newline at end of file diff --git a/rxjava/pom.xml b/rxjava/pom.xml index b3936bf78dbe..3313713b9ba8 100644 --- a/rxjava/pom.xml +++ b/rxjava/pom.xml @@ -17,6 +17,20 @@ 1.8 + + org.apache.maven.plugins + maven-surefire-plugin + + 3 + true + + **/*IntegrationTest.java + **/*LongRunningUnitTest.java + **/*ManualTest.java + + true + + @@ -47,4 +61,5 @@ 1.3 + \ No newline at end of file diff --git a/rxjava/src/test/java/com/baeldung/rxjava/RxJavaBackpressureTest.java b/rxjava/src/test/java/com/baeldung/rxjava/RxJavaBackpressureLongRunningUnitTest.java similarity index 98% rename from rxjava/src/test/java/com/baeldung/rxjava/RxJavaBackpressureTest.java rename to rxjava/src/test/java/com/baeldung/rxjava/RxJavaBackpressureLongRunningUnitTest.java index 8a495650b336..040936a67a74 100644 --- a/rxjava/src/test/java/com/baeldung/rxjava/RxJavaBackpressureTest.java +++ b/rxjava/src/test/java/com/baeldung/rxjava/RxJavaBackpressureLongRunningUnitTest.java @@ -14,7 +14,7 @@ import static org.junit.Assert.assertTrue; -public class RxJavaBackpressureTest { +public class RxJavaBackpressureLongRunningUnitTest { @Test public void givenColdObservable_shouldNotThrowException() { diff --git a/selenium-junit-testng/pom.xml b/selenium-junit-testng/pom.xml index b4490f779b48..fceeb6eec6db 100644 --- a/selenium-junit-testng/pom.xml +++ b/selenium-junit-testng/pom.xml @@ -56,6 +56,12 @@ org.seleniumhq.selenium selenium-java ${selenium-java.version} + + + commons-logging + commons-logging + + junit diff --git a/solr-fulltext-search/src/main/java/com/baeldung/solr/fulltexh/search/model/Product.java b/solr-fulltext-search/src/main/java/com/baeldung/solr/fulltexh/search/model/Product.java deleted file mode 100644 index b5fb3342823d..000000000000 --- a/solr-fulltext-search/src/main/java/com/baeldung/solr/fulltexh/search/model/Product.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.baeldung.solr.fulltexh.search.model; - -public class Product { - -} diff --git a/solr-fulltext-search/src/main/java/com/baeldung/solr/fulltexh/search/service/SolrSearchService.java b/solr-fulltext-search/src/main/java/com/baeldung/solr/fulltexh/search/service/SolrSearchService.java deleted file mode 100644 index aa511f0e1ba4..000000000000 --- a/solr-fulltext-search/src/main/java/com/baeldung/solr/fulltexh/search/service/SolrSearchService.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.baeldung.solr.fulltexh.search.service; - -public interface SolrSearchService { - -} diff --git a/solr/README.md b/solr/README.md new file mode 100644 index 000000000000..631a4f44d1f3 --- /dev/null +++ b/solr/README.md @@ -0,0 +1,3 @@ +### Relevant articles + +- [Full-text Search with Solr](http://www.baeldung.com/full-text-search-with-solr) diff --git a/solr/pom.xml b/solr/pom.xml new file mode 100644 index 000000000000..cc3a6165fe68 --- /dev/null +++ b/solr/pom.xml @@ -0,0 +1,51 @@ + + + 4.0.0 + com.baeldung + solr + 0.0.1-SNAPSHOT + jar + solr + + + + UTF-8 + 2.19.1 + + + + + org.apache.solr + solr-solrj + 6.4.1 + + + junit + junit + 4.12 + test + + + + + + + + maven-compiler-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + **/*IntegrationTest.java + **/*LiveTest.java + + + + + + + \ No newline at end of file diff --git a/solr/src/main/java/com/baeldung/solr/fulltext/search/model/Item.java b/solr/src/main/java/com/baeldung/solr/fulltext/search/model/Item.java new file mode 100644 index 000000000000..d7a0524ca92e --- /dev/null +++ b/solr/src/main/java/com/baeldung/solr/fulltext/search/model/Item.java @@ -0,0 +1,51 @@ +package com.baeldung.solr.fulltext.search.model; + +import org.apache.solr.client.solrj.beans.Field; + +public class Item { + + @Field + private String id; + + @Field + private String description; + + @Field + private String category; + + @Field + private float price; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getCategory() { + return category; + } + + public void setCategory(String category) { + this.category = category; + } + + public float getPrice() { + return price; + } + + public void setPrice(float price) { + this.price = price; + } + +} diff --git a/solr/src/main/java/com/baeldung/solr/fulltext/search/service/ItemSearchService.java b/solr/src/main/java/com/baeldung/solr/fulltext/search/service/ItemSearchService.java new file mode 100644 index 000000000000..fa906fc975ab --- /dev/null +++ b/solr/src/main/java/com/baeldung/solr/fulltext/search/service/ItemSearchService.java @@ -0,0 +1,15 @@ +package com.baeldung.solr.fulltext.search.service; + +import java.io.IOException; + +import org.apache.solr.client.solrj.SolrServerException; + +import com.baeldung.solr.fulltext.search.model.Item; + +public interface ItemSearchService { + + public void index(String id, String description, String category, float price) throws SolrServerException, IOException; + + public void indexBean(Item item) throws IOException, SolrServerException; + +} diff --git a/solr/src/main/java/com/baeldung/solr/fulltext/search/service/ItemSearchServiceImpl.java b/solr/src/main/java/com/baeldung/solr/fulltext/search/service/ItemSearchServiceImpl.java new file mode 100644 index 000000000000..573cc58bc067 --- /dev/null +++ b/solr/src/main/java/com/baeldung/solr/fulltext/search/service/ItemSearchServiceImpl.java @@ -0,0 +1,34 @@ +package com.baeldung.solr.fulltext.search.service; + +import java.io.IOException; + +import org.apache.solr.client.solrj.SolrClient; +import org.apache.solr.client.solrj.SolrServerException; +import org.apache.solr.common.SolrInputDocument; + +import com.baeldung.solr.fulltext.search.model.Item; + +public class ItemSearchServiceImpl implements ItemSearchService { + + private final SolrClient solrClient; + + public ItemSearchServiceImpl(SolrClient solrClient) { + this.solrClient = solrClient; + } + + public void index(String id, String description, String category, float price) throws SolrServerException, IOException { + SolrInputDocument doc = new SolrInputDocument(); + doc.addField("id", id); + doc.addField("description", description); + doc.addField("category", category); + doc.addField("price", price); + solrClient.add(doc); + solrClient.commit(); + } + + public void indexBean(Item item) throws IOException, SolrServerException { + solrClient.addBean(item); + solrClient.commit(); + } + +} diff --git a/solr/src/test/java/com/baeldung/solr/fulltext/search/service/ItemSearchServiceLiveTest.java b/solr/src/test/java/com/baeldung/solr/fulltext/search/service/ItemSearchServiceLiveTest.java new file mode 100644 index 000000000000..3704f4c34c0c --- /dev/null +++ b/solr/src/test/java/com/baeldung/solr/fulltext/search/service/ItemSearchServiceLiveTest.java @@ -0,0 +1,374 @@ +package com.baeldung.solr.fulltext.search.service; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.util.List; +import java.util.Map; + +import org.apache.solr.client.solrj.SolrClient; +import org.apache.solr.client.solrj.SolrQuery; +import org.apache.solr.client.solrj.impl.HttpSolrClient; +import org.apache.solr.client.solrj.response.FacetField.Count; +import org.apache.solr.client.solrj.response.QueryResponse; +import org.apache.solr.client.solrj.response.RangeFacet; +import org.apache.solr.client.solrj.response.SpellCheckResponse; +import org.apache.solr.client.solrj.response.SpellCheckResponse.Suggestion; +import org.apache.solr.client.solrj.response.SuggesterResponse; +import org.apache.solr.common.SolrDocument; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.baeldung.solr.fulltext.search.model.Item; + +public class ItemSearchServiceLiveTest { + + private static SolrClient solrClient; + private static ItemSearchService itemSearchService; + private static final String solrUrl = "http://localhost:8983/solr/item"; + + @BeforeClass + public static void initBeans() throws Exception { + solrClient = new HttpSolrClient.Builder(solrUrl).build(); + itemSearchService = new ItemSearchServiceImpl(solrClient); + + solrClient.commit(); + } + + @Before + public void clearSolrData() throws Exception { + solrClient.deleteByQuery("*:*"); + } + + @Test + public void whenIndexing_thenAvailableOnRetrieval() throws Exception { + itemSearchService.index("hm0001", "Washing Machine", "Home Appliances", 450f); + final SolrDocument indexedDoc = solrClient.getById("hm0001"); + assertEquals("hm0001", indexedDoc.get("id")); + } + + @Test + public void whenIndexingBean_thenAvailableOnRetrieval() throws Exception { + Item item = new Item(); + item.setId("hm0002"); + item.setCategory("Televisions"); + item.setDescription("LED TV 32"); + item.setPrice(500); + itemSearchService.indexBean(item); + } + + @Test + public void whenSearchingByBasicQuery_thenAllMatchingItemsShouldAvialble() throws Exception { + itemSearchService.index("hm0001", "Brand1 Washing Machine", "Home Appliances", 450f); + itemSearchService.index("hm0002", "Brand1 Refrigerator", "Home Appliances", 450f); + itemSearchService.index("hm0003", "LED TV 32", "Brand1 Home Appliances", 450f); + + SolrQuery query = new SolrQuery(); + query.setQuery("brand1"); + query.setStart(0); + query.setRows(10); + + QueryResponse response = solrClient.query(query); + List items = response.getBeans(Item.class); + + assertEquals(3, items.size()); + + } + + @Test + public void whenSearchingWithWildCard_thenAllMatchingItemsShouldAvialble() throws Exception { + itemSearchService.index("hm0001", "Brand1 Washing Machine", "Home Appliances", 450f); + itemSearchService.index("hm0002", "Brand1 Refrigerator", "Home Appliances", 450f); + itemSearchService.index("hm0003", "LED TV 32", "Brand1 Home Appliances", 450f); + + SolrQuery query = new SolrQuery(); + query.setQuery("*rand?"); + + QueryResponse response = solrClient.query(query); + List items = response.getBeans(Item.class); + + assertEquals(3, items.size()); + } + + @Test + public void whenSearchingWithLogicalOperators_thenAllMatchingItemsShouldAvialble() throws Exception { + itemSearchService.index("hm0001", "Brand1 Washing Machine", "Home Appliances", 450f); + itemSearchService.index("hm0002", "Brand1 Refrigerator", "Home Appliances", 450f); + itemSearchService.index("hm0003", "Brand2 LED TV 32", "Washing Appliances", 450f); + + SolrQuery query = new SolrQuery(); + query.setQuery("brand1 AND (Washing OR Refrigerator)"); + + QueryResponse response = solrClient.query(query); + List items = response.getBeans(Item.class); + + assertEquals(2, items.size()); + } + + @Test + public void whenSearchingWithFields_thenAllMatchingItemsShouldAvialble() throws Exception { + itemSearchService.index("0001", "Brand1 Washing Machine", "Home Appliances", 450f); + itemSearchService.index("0002", "Brand1 Refrigerator", "Home Appliances", 450f); + itemSearchService.index("0003", "Brand2 LED TV 32", "Brand1 Washing Home Appliances", 450f); + + SolrQuery query = new SolrQuery(); + query.setQuery("description:Brand* AND category:*Washing*"); + + QueryResponse response = solrClient.query(query); + List items = response.getBeans(Item.class); + + assertEquals(1, items.size()); + } + + @Test + public void whenSearchingWithPhrase_thenAllMatchingItemsShouldAvialble() throws Exception { + itemSearchService.index("hm0001", "Brand1 Washing Machine", "Home Appliances", 450f); + itemSearchService.index("hm0002", "Brand1 Refrigerator", "Home Appliances", 450f); + itemSearchService.index("hm0003", "Brand2 Dishwasher", "Washing tools and equipment ", 450f); + + SolrQuery query = new SolrQuery(); + query.setQuery("washing MachIne"); + + QueryResponse response = solrClient.query(query); + List items = response.getBeans(Item.class); + + assertEquals(2, items.size()); + } + + @Test + public void whenSearchingWithRealPhrase_thenAllMatchingItemsShouldAvialble() throws Exception { + itemSearchService.index("hm0001", "Brand1 Washing Machine", "Home Appliances", 450f); + itemSearchService.index("hm0002", "Brand1 Refrigerator", "Home Appliances", 450f); + itemSearchService.index("hm0003", "Brand2 Dishwasher", "Washing tools and equipment ", 450f); + + SolrQuery query = new SolrQuery(); + query.setQuery("\"washing machine\""); + + QueryResponse response = solrClient.query(query); + List items = response.getBeans(Item.class); + + assertEquals(1, items.size()); + } + + @Test + public void whenSearchingPhraseWithProximity_thenAllMatchingItemsShouldAvialble() throws Exception { + itemSearchService.index("hm0001", "Brand1 Washing Machine", "Home Appliances", 450f); + itemSearchService.index("hm0002", "Brand1 Refrigerator", "Home Appliances", 450f); + itemSearchService.index("hm0003", "Brand2 Dishwasher", "Washing tools and equipment ", 450f); + + SolrQuery query = new SolrQuery(); + query.setQuery("\"Washing equipment\"~2"); + + QueryResponse response = solrClient.query(query); + List items = response.getBeans(Item.class); + + assertEquals(1, items.size()); + } + + @Test + public void whenSearchingWithPriceRange_thenAllMatchingItemsShouldAvialble() throws Exception { + itemSearchService.index("hm0001", "Brand1 Washing Machine", "Home Appliances", 100f); + itemSearchService.index("hm0002", "Brand1 Refrigerator", "Home Appliances", 300f); + itemSearchService.index("hm0003", "Brand2 Dishwasher", "Home Appliances", 200f); + itemSearchService.index("hm0004", "Brand2 Dishwasher", "Washing tools and equipment ", 450f); + + SolrQuery query = new SolrQuery(); + query.setQuery("price:[100 TO 300]"); + + QueryResponse response = solrClient.query(query); + List items = response.getBeans(Item.class); + + assertEquals(3, items.size()); + } + + @Test + public void whenSearchingWithPriceRangeInclusiveExclusive_thenAllMatchingItemsShouldAvialble() throws Exception { + itemSearchService.index("hm0001", "Brand1 Washing Machine", "Home Appliances", 100f); + itemSearchService.index("hm0002", "Brand1 Refrigerator", "Home Appliances", 300f); + itemSearchService.index("hm0003", "Brand2 Dishwasher", "Home Appliances", 200f); + itemSearchService.index("hm0004", "Brand2 Dishwasher", "Washing tools and equipment ", 450f); + + SolrQuery query = new SolrQuery(); + query.setQuery("price:{100 TO 300]"); + + QueryResponse response = solrClient.query(query); + List items = response.getBeans(Item.class); + + assertEquals(2, items.size()); + } + + @Test + public void whenSearchingWithFilterQuery_thenAllMatchingItemsShouldAvialble() throws Exception { + itemSearchService.index("hm0001", "Brand1 Washing Machine", "Home Appliances", 100f); + itemSearchService.index("hm0002", "Brand1 Refrigerator", "Home Appliances", 300f); + itemSearchService.index("hm0003", "Brand2 Ceiling Fan", "Home Appliances", 200f); + itemSearchService.index("hm0004", "Brand2 Dishwasher", "Washing tools and equipment ", 250f); + + SolrQuery query = new SolrQuery(); + query.setQuery("price:[100 TO 300]"); + query.addFilterQuery("description:Brand1", "category:Home Appliances"); + + QueryResponse response = solrClient.query(query); + List items = response.getBeans(Item.class); + + assertEquals(2, items.size()); + } + + @Test + public void whenSearchingWithFacetFields_thenAllMatchingFacetsShouldAvialble() throws Exception { + itemSearchService.index("hm0001", "Brand1 Washing Machine", "CategoryA", 100f); + itemSearchService.index("hm0002", "Brand1 Refrigerator", "CategoryA", 300f); + itemSearchService.index("hm0003", "Brand2 Ceiling Fan", "CategoryB", 200f); + itemSearchService.index("hm0004", "Brand2 Dishwasher", "CategoryB", 250f); + + SolrQuery query = new SolrQuery(); + query.setQuery("*:*"); + query.addFacetField("category"); + + QueryResponse response = solrClient.query(query); + List facetResults = response.getFacetField("category").getValues(); + + assertEquals(2, facetResults.size()); + + for (Count count : facetResults) { + if ("categorya".equalsIgnoreCase(count.getName())) { + assertEquals(2, count.getCount()); + } else if ("categoryb".equalsIgnoreCase(count.getName())) { + assertEquals(2, count.getCount()); + } else { + fail("unexpected category"); + } + } + } + + @Test + public void whenSearchingWithFacetQuery_thenAllMatchingFacetsShouldAvialble() throws Exception { + itemSearchService.index("hm0001", "Brand1 Washing Machine", "CategoryA", 100f); + itemSearchService.index("hm0002", "Brand1 Refrigerator", "CategoryA", 300f); + itemSearchService.index("hm0003", "Brand2 Ceiling Fan", "CategoryB", 200f); + itemSearchService.index("hm0004", "Brand2 Dishwasher", "CategoryB", 250f); + + SolrQuery query = new SolrQuery(); + query.setQuery("*:*"); + + query.addFacetQuery("Washing OR Refrigerator"); + query.addFacetQuery("Brand2"); + + QueryResponse response = solrClient.query(query); + Map facetQueryMap = response.getFacetQuery(); + + assertEquals(2, facetQueryMap.size()); + + for (Map.Entry entry : facetQueryMap.entrySet()) { + String facetQuery = entry.getKey(); + + if ("Washing OR Refrigerator".equals(facetQuery)) { + assertEquals(Integer.valueOf(2), entry.getValue()); + } else if ("Brand2".equals(facetQuery)) { + assertEquals(Integer.valueOf(2), entry.getValue()); + } else { + fail("unexpected query"); + } + + } + } + + @Test + public void whenSearchingWithFacetRange_thenAllMatchingFacetsShouldAvialble() throws Exception { + itemSearchService.index("hm0001", "Brand1 Washing Machine", "CategoryA", 100f); + itemSearchService.index("hm0002", "Brand1 Refrigerator", "CategoryA", 125f); + itemSearchService.index("hm0003", "Brand2 Ceiling Fan", "CategoryB", 150f); + itemSearchService.index("hm0004", "Brand2 Dishwasher", "CategoryB", 250f); + + SolrQuery query = new SolrQuery(); + query.setQuery("*:*"); + + query.addNumericRangeFacet("price", 100, 275, 25); + + QueryResponse response = solrClient.query(query); + List rangeFacets = response.getFacetRanges().get(0).getCounts(); + + assertEquals(7, rangeFacets.size()); + } + + @Test + public void whenSearchingWithHitHighlighting_thenKeywordsShouldBeHighlighted() throws Exception { + itemSearchService.index("hm0001", "Brand1 Washing Machine", "Home Appliances", 100f); + itemSearchService.index("hm0002", "Brand1 Refrigerator", "Home Appliances", 300f); + itemSearchService.index("hm0003", "Brand2 Ceiling Fan", "Home Appliances", 200f); + itemSearchService.index("hm0004", "Brand2 Dishwasher", "Washing equipments", 250f); + + SolrQuery query = new SolrQuery(); + query.setQuery("Appliances"); + query.setHighlight(true); + query.addHighlightField("category"); + query.setHighlightSimplePre(""); + query.setHighlightSimplePost(""); + QueryResponse response = solrClient.query(query); + + Map>> hitHighlightedMap = response.getHighlighting(); + Map> highlightedFieldMap = hitHighlightedMap.get("hm0001"); + List highlightedList = highlightedFieldMap.get("category"); + String highLightedText = highlightedList.get(0); + + assertEquals("Home Appliances", highLightedText); + + } + + @Test + public void whenSearchingWithKeywordWithMistake_thenSpellingSuggestionsShouldBeReturned() throws Exception { + itemSearchService.index("hm0001", "Brand1 Washing Machine", "Home Appliances", 100f); + itemSearchService.index("hm0002", "Brand1 Refrigerator", "Home Appliances", 300f); + itemSearchService.index("hm0003", "Brand2 Ceiling Fan", "Home Appliances", 200f); + itemSearchService.index("hm0004", "Brand2 Dishwasher", "Washing equipments", 250f); + + SolrQuery query = new SolrQuery(); + query.setQuery("hme"); + query.set("spellcheck", "on"); + QueryResponse response = solrClient.query(query); + + SpellCheckResponse spellCheckResponse = response.getSpellCheckResponse(); + + assertEquals(false, spellCheckResponse.isCorrectlySpelled()); + + Suggestion suggestion = spellCheckResponse.getSuggestions().get(0); + + assertEquals("hme", suggestion.getToken()); + + List alternatives = suggestion.getAlternatives(); + String alternative = alternatives.get(0); + + assertEquals("home", alternative); + } + + @Test + public void whenSearchingWithIncompleteKeyword_thenKeywordSuggestionsShouldBeReturned() throws Exception { + itemSearchService.index("hm0001", "Brand1 Washing Machine", "Home Appliances", 100f); + itemSearchService.index("hm0002", "Brand1 Refrigerator", "Home Appliances", 300f); + itemSearchService.index("hm0003", "Brand2 Ceiling Fan", "Home Appliances", 200f); + itemSearchService.index("hm0004", "Brand2 Dishwasher", "Home washing equipments", 250f); + + SolrQuery query = new SolrQuery(); + query.setRequestHandler("/suggest"); + query.set("suggest", "true"); + query.set("suggest.build", "true"); + query.set("suggest.dictionary", "mySuggester"); + query.set("suggest.q", "Hom"); + QueryResponse response = solrClient.query(query); + + SuggesterResponse suggesterResponse = response.getSuggesterResponse(); + Map> suggestedTerms = suggesterResponse.getSuggestedTerms(); + List suggestions = suggestedTerms.get("mySuggester"); + + assertEquals(2, suggestions.size()); + + for (String term : suggestions) { + if (!"Home Appliances".equals(term) && !"Home washing equipments".equals(term)) { + fail("Unexpected suggestions"); + } + } + + } + +} diff --git a/spring-5/README.md b/spring-5/README.md index 0914388b49bc..31f665f4a2c5 100644 --- a/spring-5/README.md +++ b/spring-5/README.md @@ -1,6 +1,9 @@ ## Spring REST Example Project -###The Course +### The Course The "REST With Spring" Classes: http://bit.ly/restwithspring -### Relevant Articles: +### Relevant Articles + +- [Concurrent Test Execution in Spring 5](http://www.baeldung.com/spring-5-concurrent-tests) +- [Introduction to the Functional Web Framework in Spring 5](http://www.baeldung.com/spring-5-functional-web) diff --git a/spring-5/pom.xml b/spring-5/pom.xml index ab05918ae4de..ec55a878c784 100644 --- a/spring-5/pom.xml +++ b/spring-5/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 com.baeldung @@ -9,13 +9,13 @@ jar spring-5 - + spring 5 sample project about new features org.springframework.boot spring-boot-starter-parent 2.0.0.BUILD-SNAPSHOT - + @@ -35,6 +35,10 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.boot + spring-boot-starter-webflux + @@ -54,11 +58,39 @@ h2 runtime + + org.junit.jupiter + junit-jupiter-api + ${junit.jupiter.version} + + + org.springframework + spring-test + ${spring.test.version} + org.springframework.boot spring-boot-starter-test test + + org.junit.jupiter + junit-jupiter-engine + ${junit.jupiter.version} + test + + + org.junit.platform + junit-platform-surefire-provider + ${junit.platform.version} + test + + + org.junit.platform + junit-platform-runner + ${junit.platform.version} + test + @@ -66,7 +98,35 @@ org.springframework.boot spring-boot-maven-plugin + + com.baeldung.Spring5Application + JAR + + + + org.apache.maven.plugins + maven-surefire-plugin + + 3 + true + + **/*IntegrationTest.java + **/*LiveTest.java + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + methods + true + + + @@ -111,6 +171,10 @@ UTF-8 UTF-8 1.8 + 1.0.0-M3 + 5.0.0-M3 + 4.3.7.RELEASE + 2.19.1 diff --git a/spring-5/src/main/java/com/baeldung/functional/Actor.java b/spring-5/src/main/java/com/baeldung/functional/Actor.java new file mode 100644 index 000000000000..23c88b89e125 --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/functional/Actor.java @@ -0,0 +1,23 @@ +package com.baeldung.functional; + +class Actor { + private String firstname; + private String lastname; + + public Actor() { + } + + public Actor(String firstname, String lastname) { + this.firstname = firstname; + this.lastname = lastname; + } + + public String getFirstname() { + return firstname; + } + + public String getLastname() { + return lastname; + } + +} diff --git a/spring-5/src/main/java/com/baeldung/functional/FormHandler.java b/spring-5/src/main/java/com/baeldung/functional/FormHandler.java new file mode 100644 index 000000000000..c44db76f5615 --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/functional/FormHandler.java @@ -0,0 +1,50 @@ +package com.baeldung.functional; + +import org.springframework.util.MultiValueMap; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Mono; + +import java.util.concurrent.atomic.AtomicLong; + +import static org.springframework.web.reactive.function.BodyExtractors.toDataBuffers; +import static org.springframework.web.reactive.function.BodyExtractors.toFormData; +import static org.springframework.web.reactive.function.BodyInserters.fromObject; +import static org.springframework.web.reactive.function.server.ServerResponse.ok; + +public class FormHandler { + + Mono handleLogin(ServerRequest request) { + return request + .body(toFormData()) + .map(MultiValueMap::toSingleValueMap) + .map(formData -> { + System.out.println("form data: " + formData.toString()); + if ("baeldung".equals(formData.get("user")) && "you_know_what_to_do".equals(formData.get("token"))) { + return ok() + .body(Mono.just("welcome back!"), String.class) + .block(); + } + return ServerResponse + .badRequest() + .build() + .block(); + }); + } + + Mono handleUpload(ServerRequest request) { + return request + .body(toDataBuffers()) + .collectList() + .map(dataBuffers -> { + AtomicLong atomicLong = new AtomicLong(0); + dataBuffers.forEach(d -> atomicLong.addAndGet(d + .asByteBuffer() + .array().length)); + System.out.println("data length:" + atomicLong.get()); + return ok() + .body(fromObject(atomicLong.toString())) + .block(); + }); + } +} diff --git a/spring-5/src/main/java/com/baeldung/functional/FunctionalSpringBootApplication.java b/spring-5/src/main/java/com/baeldung/functional/FunctionalSpringBootApplication.java new file mode 100644 index 000000000000..258a23a2bf3b --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/functional/FunctionalSpringBootApplication.java @@ -0,0 +1,87 @@ +package com.baeldung.functional; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.servlet.ServletRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.core.io.ClassPathResource; +import org.springframework.http.server.reactive.HttpHandler; +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; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerResponse; +import org.springframework.web.server.adapter.WebHttpHandlerBuilder; +import reactor.core.publisher.Flux; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import static org.springframework.web.reactive.function.BodyInserters.fromObject; +import static org.springframework.web.reactive.function.server.RequestPredicates.*; +import static org.springframework.web.reactive.function.server.RouterFunctions.route; +import static org.springframework.web.reactive.function.server.RouterFunctions.toHttpHandler; +import static org.springframework.web.reactive.function.server.ServerResponse.ok; + +@SpringBootApplication +@ComponentScan(basePackages = { "com.baeldung.functional" }) +public class FunctionalSpringBootApplication { + + private static final Actor BRAD_PITT = new Actor("Brad", "Pitt"); + private static final Actor TOM_HANKS = new Actor("Tom", "Hanks"); + private static final List actors = new CopyOnWriteArrayList<>(Arrays.asList(BRAD_PITT, TOM_HANKS)); + + private RouterFunction routingFunction() { + FormHandler formHandler = new FormHandler(); + + RouterFunction restfulRouter = route(GET("/"), serverRequest -> ok().body(Flux.fromIterable(actors), Actor.class)).andRoute(POST("/"), serverRequest -> serverRequest + .bodyToMono(Actor.class) + .doOnNext(actors::add) + .then(ok().build())); + + return route(GET("/test"), serverRequest -> ok().body(fromObject("helloworld"))) + .andRoute(POST("/login"), formHandler::handleLogin) + .andRoute(POST("/upload"), formHandler::handleUpload) + .and(RouterFunctions.resources("/files/**", new ClassPathResource("files/"))) + .andNest(path("/actor"), restfulRouter) + .filter((request, next) -> { + System.out.println("Before handler invocation: " + request.path()); + return next.handle(request); + }); + } + + @Bean + public ServletRegistrationBean servletRegistrationBean() throws Exception { + HttpHandler httpHandler = WebHttpHandlerBuilder + .webHandler(toHttpHandler(routingFunction())) + .prependFilter(new IndexRewriteFilter()) + .build(); + ServletRegistrationBean registrationBean = new ServletRegistrationBean<>(new RootServlet(httpHandler), "/"); + registrationBean.setLoadOnStartup(1); + registrationBean.setAsyncSupported(true); + return registrationBean; + } + + @Configuration + @EnableWebSecurity + @Profile("!https") + static class SecurityConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(final HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest() + .permitAll(); + } + } + + public static void main(String[] args) { + SpringApplication.run(FunctionalSpringBootApplication.class, args); + } + +} diff --git a/spring-5/src/main/java/com/baeldung/functional/FunctionalWebApplication.java b/spring-5/src/main/java/com/baeldung/functional/FunctionalWebApplication.java new file mode 100644 index 000000000000..573813b166f5 --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/functional/FunctionalWebApplication.java @@ -0,0 +1,80 @@ +package com.baeldung.functional; + +import org.apache.catalina.Context; +import org.apache.catalina.startup.Tomcat; +import org.springframework.boot.web.embedded.tomcat.TomcatWebServer; +import org.springframework.boot.web.server.WebServer; +import org.springframework.core.io.ClassPathResource; +import org.springframework.http.server.reactive.HttpHandler; +import org.springframework.http.server.reactive.ServletHttpHandlerAdapter; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerResponse; +import org.springframework.web.server.WebHandler; +import org.springframework.web.server.adapter.WebHttpHandlerBuilder; +import reactor.core.publisher.Flux; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import static org.springframework.web.reactive.function.BodyInserters.fromObject; +import static org.springframework.web.reactive.function.server.RequestPredicates.*; +import static org.springframework.web.reactive.function.server.RouterFunctions.route; +import static org.springframework.web.reactive.function.server.RouterFunctions.toHttpHandler; +import static org.springframework.web.reactive.function.server.ServerResponse.ok; + +public class FunctionalWebApplication { + + private static final Actor BRAD_PITT = new Actor("Brad", "Pitt"); + private static final Actor TOM_HANKS = new Actor("Tom", "Hanks"); + private static final List actors = new CopyOnWriteArrayList<>(Arrays.asList(BRAD_PITT, TOM_HANKS)); + + private RouterFunction routingFunction() { + FormHandler formHandler = new FormHandler(); + + RouterFunction restfulRouter = route(GET("/"), serverRequest -> ok().body(Flux.fromIterable(actors), Actor.class)).andRoute(POST("/"), serverRequest -> serverRequest + .bodyToMono(Actor.class) + .doOnNext(actors::add) + .then(ok().build())); + + return route(GET("/test"), serverRequest -> ok().body(fromObject("helloworld"))) + .andRoute(POST("/login"), formHandler::handleLogin) + .andRoute(POST("/upload"), formHandler::handleUpload) + .and(RouterFunctions.resources("/files/**", new ClassPathResource("files/"))) + .andNest(path("/actor"), restfulRouter) + .filter((request, next) -> { + System.out.println("Before handler invocation: " + request.path()); + return next.handle(request); + }); + } + + WebServer start() throws Exception { + WebHandler webHandler = toHttpHandler(routingFunction()); + HttpHandler httpHandler = WebHttpHandlerBuilder + .webHandler(webHandler) + .prependFilter(new IndexRewriteFilter()) + .build(); + + Tomcat tomcat = new Tomcat(); + tomcat.setHostname("localhost"); + tomcat.setPort(9090); + Context rootContext = tomcat.addContext("", System.getProperty("java.io.tmpdir")); + ServletHttpHandlerAdapter servlet = new ServletHttpHandlerAdapter(httpHandler); + Tomcat.addServlet(rootContext, "httpHandlerServlet", servlet); + rootContext.addServletMappingDecoded("/", "httpHandlerServlet"); + + TomcatWebServer server = new TomcatWebServer(tomcat); + server.start(); + return server; + + } + + public static void main(String[] args) { + try { + new FunctionalWebApplication().start(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/spring-5/src/main/java/com/baeldung/functional/IndexRewriteFilter.java b/spring-5/src/main/java/com/baeldung/functional/IndexRewriteFilter.java new file mode 100644 index 000000000000..3e19f819433a --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/functional/IndexRewriteFilter.java @@ -0,0 +1,29 @@ +package com.baeldung.functional; + +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebFilter; +import org.springframework.web.server.WebFilterChain; +import reactor.core.publisher.Mono; + +class IndexRewriteFilter implements WebFilter { + + @Override + public Mono filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) { + ServerHttpRequest request = serverWebExchange.getRequest(); + if (request + .getURI() + .getPath() + .equals("/")) { + return webFilterChain.filter(serverWebExchange + .mutate() + .request(builder -> builder + .method(request.getMethod()) + .contextPath(request.getContextPath()) + .path("/test")) + .build()); + } + return webFilterChain.filter(serverWebExchange); + } + +} diff --git a/spring-5/src/main/java/com/baeldung/functional/RootServlet.java b/spring-5/src/main/java/com/baeldung/functional/RootServlet.java new file mode 100644 index 000000000000..54892c829f2e --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/functional/RootServlet.java @@ -0,0 +1,87 @@ +package com.baeldung.functional; + +import org.springframework.core.io.ClassPathResource; +import org.springframework.http.server.reactive.HttpHandler; +import org.springframework.http.server.reactive.ServletHttpHandlerAdapter; +import org.springframework.util.MultiValueMap; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerResponse; +import org.springframework.web.server.adapter.WebHttpHandlerBuilder; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicLong; + +import static org.springframework.web.reactive.function.BodyExtractors.toDataBuffers; +import static org.springframework.web.reactive.function.BodyExtractors.toFormData; +import static org.springframework.web.reactive.function.BodyInserters.fromObject; +import static org.springframework.web.reactive.function.server.RequestPredicates.*; +import static org.springframework.web.reactive.function.server.RouterFunctions.route; +import static org.springframework.web.reactive.function.server.RouterFunctions.toHttpHandler; +import static org.springframework.web.reactive.function.server.ServerResponse.ok; + +public class RootServlet extends ServletHttpHandlerAdapter { + + public RootServlet() { + this(WebHttpHandlerBuilder + .webHandler(toHttpHandler(routingFunction())) + .prependFilter(new IndexRewriteFilter()) + .build()); + } + + RootServlet(HttpHandler httpHandler) { + super(httpHandler); + } + + private static final Actor BRAD_PITT = new Actor("Brad", "Pitt"); + private static final Actor TOM_HANKS = new Actor("Tom", "Hanks"); + private static final List actors = new CopyOnWriteArrayList<>(Arrays.asList(BRAD_PITT, TOM_HANKS)); + + private static RouterFunction routingFunction() { + + return route(GET("/test"), serverRequest -> ok().body(fromObject("helloworld"))) + .andRoute(POST("/login"), serverRequest -> serverRequest + .body(toFormData()) + .map(MultiValueMap::toSingleValueMap) + .map(formData -> { + System.out.println("form data: " + formData.toString()); + if ("baeldung".equals(formData.get("user")) && "you_know_what_to_do".equals(formData.get("token"))) { + return ok() + .body(Mono.just("welcome back!"), String.class) + .block(); + } + return ServerResponse + .badRequest() + .build() + .block(); + })) + .andRoute(POST("/upload"), serverRequest -> serverRequest + .body(toDataBuffers()) + .collectList() + .map(dataBuffers -> { + AtomicLong atomicLong = new AtomicLong(0); + dataBuffers.forEach(d -> atomicLong.addAndGet(d + .asByteBuffer() + .array().length)); + System.out.println("data length:" + atomicLong.get()); + return ok() + .body(fromObject(atomicLong.toString())) + .block(); + })) + .and(RouterFunctions.resources("/files/**", new ClassPathResource("files/"))) + .andNest(path("/actor"), route(GET("/"), serverRequest -> ok().body(Flux.fromIterable(actors), Actor.class)).andRoute(POST("/"), serverRequest -> serverRequest + .bodyToMono(Actor.class) + .doOnNext(actors::add) + .then(ok().build()))) + .filter((request, next) -> { + System.out.println("Before handler invocation: " + request.path()); + return next.handle(request); + }); + + } + +} diff --git a/spring-5/src/main/java/com/baeldung/jupiter/MethodParameterFactory.java b/spring-5/src/main/java/com/baeldung/jupiter/MethodParameterFactory.java new file mode 100644 index 000000000000..85bd505d1167 --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/jupiter/MethodParameterFactory.java @@ -0,0 +1,46 @@ +package com.baeldung.jupiter; + +import org.springframework.core.MethodParameter; +import org.springframework.core.annotation.SynthesizingMethodParameter; +import org.springframework.util.Assert; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; + +abstract class MethodParameterFactory { + + private MethodParameterFactory() { + } + + public static MethodParameter createMethodParameter(Parameter parameter) { + Assert.notNull(parameter, "Parameter must not be null"); + Executable executable = parameter.getDeclaringExecutable(); + if (executable instanceof Method) { + return new MethodParameter((Method) executable, getIndex(parameter)); + } + return new MethodParameter((Constructor) executable, getIndex(parameter)); + } + + public static SynthesizingMethodParameter createSynthesizingMethodParameter(Parameter parameter) { + Assert.notNull(parameter, "Parameter must not be null"); + Executable executable = parameter.getDeclaringExecutable(); + if (executable instanceof Method) { + return new SynthesizingMethodParameter((Method) executable, getIndex(parameter)); + } + throw new UnsupportedOperationException("Cannot create a SynthesizingMethodParameter for a constructor parameter: " + parameter); + } + + private static int getIndex(Parameter parameter) { + Assert.notNull(parameter, "Parameter must not be null"); + Executable executable = parameter.getDeclaringExecutable(); + Parameter[] parameters = executable.getParameters(); + for (int i = 0; i < parameters.length; i++) { + if (parameters[i] == parameter) { + return i; + } + } + throw new IllegalStateException(String.format("Failed to resolve index of parameter [%s] in executable [%s]", parameter, executable.toGenericString())); + } +} diff --git a/spring-5/src/main/java/com/baeldung/jupiter/ParameterAutowireUtils.java b/spring-5/src/main/java/com/baeldung/jupiter/ParameterAutowireUtils.java new file mode 100644 index 000000000000..068c6af3815d --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/jupiter/ParameterAutowireUtils.java @@ -0,0 +1,46 @@ +package com.baeldung.jupiter; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.config.DependencyDescriptor; +import org.springframework.context.ApplicationContext; +import org.springframework.core.MethodParameter; +import org.springframework.core.annotation.AnnotatedElementUtils; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.Optional; + +import static org.springframework.core.annotation.AnnotatedElementUtils.hasAnnotation; + +abstract class ParameterAutowireUtils { + + private ParameterAutowireUtils() { + } + + public static boolean isAutowirable(Parameter parameter) { + return ApplicationContext.class.isAssignableFrom(parameter.getType()) || hasAnnotation(parameter, Autowired.class) || hasAnnotation(parameter, Qualifier.class) || hasAnnotation(parameter, Value.class); + } + + public static Object resolveDependency(Parameter parameter, Class containingClass, ApplicationContext applicationContext) { + + boolean required = findMergedAnnotation(parameter, Autowired.class) + .map(Autowired::required) + .orElse(true); + MethodParameter methodParameter = (parameter.getDeclaringExecutable() instanceof Method ? MethodParameterFactory.createSynthesizingMethodParameter(parameter) : MethodParameterFactory.createMethodParameter(parameter)); + DependencyDescriptor descriptor = new DependencyDescriptor(methodParameter, required); + descriptor.setContainingClass(containingClass); + + return applicationContext + .getAutowireCapableBeanFactory() + .resolveDependency(descriptor, null); + } + + private static Optional findMergedAnnotation(AnnotatedElement element, Class annotationType) { + + return Optional.ofNullable(AnnotatedElementUtils.findMergedAnnotation(element, annotationType)); + } +} diff --git a/spring-5/src/main/java/com/baeldung/jupiter/SpringExtension.java b/spring-5/src/main/java/com/baeldung/jupiter/SpringExtension.java new file mode 100644 index 000000000000..08fa0c476894 --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/jupiter/SpringExtension.java @@ -0,0 +1,94 @@ +package com.baeldung.jupiter; + +import org.junit.jupiter.api.extension.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.test.context.TestContextManager; +import org.springframework.util.Assert; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; + +public class SpringExtension implements BeforeAllCallback, AfterAllCallback, TestInstancePostProcessor, BeforeEachCallback, AfterEachCallback, ParameterResolver { + + private static final ExtensionContext.Namespace namespace = ExtensionContext.Namespace.create(SpringExtension.class); + + @Override + public void beforeAll(ContainerExtensionContext context) throws Exception { + getTestContextManager(context).beforeTestClass(); + } + + @Override + public void afterAll(ContainerExtensionContext context) throws Exception { + try { + getTestContextManager(context).afterTestClass(); + } finally { + context + .getStore(namespace) + .remove(context + .getTestClass() + .get()); + } + } + + @Override + public void postProcessTestInstance(Object testInstance, ExtensionContext context) throws Exception { + getTestContextManager(context).prepareTestInstance(testInstance); + } + + @Override + public void beforeEach(TestExtensionContext context) throws Exception { + Object testInstance = context.getTestInstance(); + Method testMethod = context + .getTestMethod() + .get(); + getTestContextManager(context).beforeTestMethod(testInstance, testMethod); + } + + @Override + public void afterEach(TestExtensionContext context) throws Exception { + Object testInstance = context.getTestInstance(); + Method testMethod = context + .getTestMethod() + .get(); + Throwable testException = context + .getTestException() + .orElse(null); + getTestContextManager(context).afterTestMethod(testInstance, testMethod, testException); + } + + @Override + public boolean supports(ParameterContext parameterContext, ExtensionContext extensionContext) { + Parameter parameter = parameterContext.getParameter(); + Executable executable = parameter.getDeclaringExecutable(); + return (executable instanceof Constructor && AnnotatedElementUtils.hasAnnotation(executable, Autowired.class)) || ParameterAutowireUtils.isAutowirable(parameter); + } + + @Override + public Object resolve(ParameterContext parameterContext, ExtensionContext extensionContext) { + Parameter parameter = parameterContext.getParameter(); + Class testClass = extensionContext + .getTestClass() + .get(); + ApplicationContext applicationContext = getApplicationContext(extensionContext); + return ParameterAutowireUtils.resolveDependency(parameter, testClass, applicationContext); + } + + private ApplicationContext getApplicationContext(ExtensionContext context) { + return getTestContextManager(context) + .getTestContext() + .getApplicationContext(); + } + + private TestContextManager getTestContextManager(ExtensionContext context) { + Assert.notNull(context, "ExtensionContext must not be null"); + Class testClass = context + .getTestClass() + .get(); + ExtensionContext.Store store = context.getStore(namespace); + return store.getOrComputeIfAbsent(testClass, TestContextManager::new, TestContextManager.class); + } +} diff --git a/spring-5/src/main/java/com/baeldung/jupiter/SpringJUnit5Config.java b/spring-5/src/main/java/com/baeldung/jupiter/SpringJUnit5Config.java new file mode 100644 index 000000000000..e8b9cc500fdf --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/jupiter/SpringJUnit5Config.java @@ -0,0 +1,40 @@ +package com.baeldung.jupiter; + +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.annotation.AliasFor; +import org.springframework.test.context.ContextConfiguration; + +import java.lang.annotation.*; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration +@Documented +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface SpringJUnit5Config { + + @AliasFor(annotation = ContextConfiguration.class, attribute = "classes") + Class[] value() default {}; + + @AliasFor(annotation = ContextConfiguration.class) + Class[] classes() default {}; + + @AliasFor(annotation = ContextConfiguration.class) + String[] locations() default {}; + + @AliasFor(annotation = ContextConfiguration.class) + Class>[] initializers() default {}; + + @AliasFor(annotation = ContextConfiguration.class) + boolean inheritLocations() default true; + + @AliasFor(annotation = ContextConfiguration.class) + boolean inheritInitializers() default true; + + @AliasFor(annotation = ContextConfiguration.class) + String name() default ""; +} diff --git a/spring-5/src/main/java/com/baeldung/jupiter/TestConfig.java b/spring-5/src/main/java/com/baeldung/jupiter/TestConfig.java new file mode 100644 index 000000000000..a29f77c5dfa3 --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/jupiter/TestConfig.java @@ -0,0 +1,20 @@ +package com.baeldung.jupiter; + +import com.baeldung.web.reactive.Task; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; + +@Configuration +public class TestConfig { + + @Bean + static PropertySourcesPlaceholderConfigurer placeholderConfigurer() { + return new PropertySourcesPlaceholderConfigurer(); + } + + @Bean + Task taskName() { + return new Task("taskName", 1); + } +} diff --git a/spring-5/src/main/java/com/baeldung/web/FooController.java b/spring-5/src/main/java/com/baeldung/web/FooController.java index de6928033efa..d0b69e707e60 100644 --- a/spring-5/src/main/java/com/baeldung/web/FooController.java +++ b/spring-5/src/main/java/com/baeldung/web/FooController.java @@ -1,6 +1,7 @@ package com.baeldung.web; import java.util.List; +import java.util.Optional; import javax.validation.constraints.Max; import javax.validation.constraints.Min; @@ -33,7 +34,7 @@ public class FooController { @ResponseBody @Validated public Foo findById(@PathVariable @Min(0) final long id) { - return repo.findOne(id); + return repo.findOne(id).orElse(null); } @RequestMapping(method = RequestMethod.GET) diff --git a/spring-5/src/main/java/com/baeldung/web/reactive/Task.java b/spring-5/src/main/java/com/baeldung/web/reactive/Task.java new file mode 100644 index 000000000000..84193d9354a8 --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/web/reactive/Task.java @@ -0,0 +1,31 @@ +package com.baeldung.web.reactive; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class Task { + + private final String name; + + private final int id; + + public Task(@JsonProperty("name") String name, @JsonProperty("id") int id) { + this.name = name; + this.id = id; + } + + public String getName() { + return this.name; + } + + public int getId() { + return this.id; + } + + @Override + public String toString() { + return "Task{" + + "name='" + name + '\'' + + ", id=" + id + + '}'; + } +} diff --git a/spring-5/src/main/resources/files/hello.txt b/spring-5/src/main/resources/files/hello.txt new file mode 100644 index 000000000000..b6fc4c620b67 --- /dev/null +++ b/spring-5/src/main/resources/files/hello.txt @@ -0,0 +1 @@ +hello \ No newline at end of file diff --git a/spring-5/src/main/webapp/WEB-INF/web.xml b/spring-5/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000000..bfcf43dad26e --- /dev/null +++ b/spring-5/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,21 @@ + + + + Spring Functional Application + + + functional + com.baeldung.functional.RootServlet + 1 + true + + + functional + / + + + + \ No newline at end of file diff --git a/spring-5/src/test/java/com/baeldung/Spring5JUnit4ConcurrentIntegrationTest.java b/spring-5/src/test/java/com/baeldung/Spring5JUnit4ConcurrentIntegrationTest.java new file mode 100644 index 000000000000..938ee7fd4361 --- /dev/null +++ b/spring-5/src/test/java/com/baeldung/Spring5JUnit4ConcurrentIntegrationTest.java @@ -0,0 +1,57 @@ +package com.baeldung; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * @author aiet + */ +@RunWith(SpringRunner.class) +@ContextConfiguration(classes = Spring5JUnit4ConcurrentIntegrationTest.SimpleConfiguration.class) +public class Spring5JUnit4ConcurrentIntegrationTest implements ApplicationContextAware, InitializingBean { + + @Configuration + public static class SimpleConfiguration { + } + + private ApplicationContext applicationContext; + + private boolean beanInitialized = false; + + @Override + public final void afterPropertiesSet() throws Exception { + this.beanInitialized = true; + } + + @Override + public final void setApplicationContext(final ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + + @Test + public final void verifyApplicationContextSet() throws InterruptedException { + TimeUnit.SECONDS.sleep(2); + assertNotNull("The application context should have been set due to ApplicationContextAware semantics.", this.applicationContext); + } + + @Test + public final void verifyBeanInitialized() throws InterruptedException { + TimeUnit.SECONDS.sleep(2); + assertTrue("This test bean should have been initialized due to InitializingBean semantics.", this.beanInitialized); + } + +} + + diff --git a/spring-5/src/test/java/com/baeldung/functional/FunctionalWebApplicationIntegrationTest.java b/spring-5/src/test/java/com/baeldung/functional/FunctionalWebApplicationIntegrationTest.java new file mode 100644 index 000000000000..dda25f46f743 --- /dev/null +++ b/spring-5/src/test/java/com/baeldung/functional/FunctionalWebApplicationIntegrationTest.java @@ -0,0 +1,155 @@ +package com.baeldung.functional; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.springframework.boot.web.server.WebServer; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.http.MediaType; +//import org.springframework.test.web.reactive.server.WebTestClient; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.reactive.function.BodyInserters; + +import static org.springframework.web.reactive.function.BodyInserters.fromObject; +import static org.springframework.web.reactive.function.BodyInserters.fromResource; + +// TODO The class does not compile, WebTestClient cannot be resolved. Missing dependency? +public class FunctionalWebApplicationIntegrationTest { + +// private static WebTestClient client; +// private static WebServer server; +// +// @BeforeClass +// public static void setup() throws Exception { +// server = new FunctionalWebApplication().start(); +// client = WebTestClient +// .bindToServer() +// .baseUrl("http://localhost:" + server.getPort()) +// .build(); +// } +// +// @AfterClass +// public static void destroy() { +// server.stop(); +// } +// +// @Test +// public void givenRouter_whenGetTest_thenGotHelloWorld() throws Exception { +// client +// .get() +// .uri("/test") +// .exchange() +// .expectStatus() +// .isOk() +// .expectBody(String.class) +// .value() +// .isEqualTo("helloworld"); +// } +// +// @Test +// public void givenIndexFilter_whenRequestRoot_thenRewrittenToTest() throws Exception { +// client +// .get() +// .uri("/") +// .exchange() +// .expectStatus() +// .isOk() +// .expectBody(String.class) +// .value() +// .isEqualTo("helloworld"); +// } +// +// @Test +// public void givenLoginForm_whenPostValidToken_thenSuccess() throws Exception { +// MultiValueMap formData = new LinkedMultiValueMap<>(1); +// formData.add("user", "baeldung"); +// formData.add("token", "you_know_what_to_do"); +// +// client +// .post() +// .uri("/login") +// .contentType(MediaType.APPLICATION_FORM_URLENCODED) +// .exchange(BodyInserters.fromFormData(formData)) +// .expectStatus() +// .isOk() +// .expectBody(String.class) +// .value() +// .isEqualTo("welcome back!"); +// } +// +// @Test +// public void givenLoginForm_whenRequestWithInvalidToken_thenFail() throws Exception { +// MultiValueMap formData = new LinkedMultiValueMap<>(2); +// formData.add("user", "baeldung"); +// formData.add("token", "try_again"); +// +// client +// .post() +// .uri("/login") +// .contentType(MediaType.APPLICATION_FORM_URLENCODED) +// .exchange(BodyInserters.fromFormData(formData)) +// .expectStatus() +// .isBadRequest(); +// } +// +// @Test +// public void givenUploadForm_whenRequestWithMultipartData_thenSuccess() throws Exception { +// Resource resource = new ClassPathResource("/baeldung-weekly.png"); +// client +// .post() +// .uri("/upload") +// .contentType(MediaType.MULTIPART_FORM_DATA) +// .exchange(fromResource(resource)) +// .expectStatus() +// .isOk() +// .expectBody(String.class) +// .value() +// .isEqualTo(String.valueOf(resource.contentLength())); +// } +// +// @Test +// public void givenActors_whenAddActor_thenAdded() throws Exception { +// client +// .get() +// .uri("/actor") +// .exchange() +// .expectStatus() +// .isOk() +// .expectBody(Actor.class) +// .list() +// .hasSize(2); +// +// client +// .post() +// .uri("/actor") +// .exchange(fromObject(new Actor("Clint", "Eastwood"))) +// .expectStatus() +// .isOk(); +// +// client +// .get() +// .uri("/actor") +// .exchange() +// .expectStatus() +// .isOk() +// .expectBody(Actor.class) +// .list() +// .hasSize(3); +// } +// +// @Test +// public void givenResources_whenAccess_thenGot() throws Exception { +// client +// .get() +// .uri("/files/hello.txt") +// .exchange() +// .expectStatus() +// .isOk() +// .expectBody(String.class) +// .value() +// .isEqualTo("hello"); +// } + +} diff --git a/spring-5/src/test/java/com/baeldung/jupiter/Spring5JUnit5ComposedAnnotationTests.java b/spring-5/src/test/java/com/baeldung/jupiter/Spring5JUnit5ComposedAnnotationTests.java new file mode 100644 index 000000000000..d15857e0a5fd --- /dev/null +++ b/spring-5/src/test/java/com/baeldung/jupiter/Spring5JUnit5ComposedAnnotationTests.java @@ -0,0 +1,38 @@ +package com.baeldung.jupiter; + +import com.baeldung.web.reactive.Task; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@SpringJUnit5Config(TestConfig.class) +@DisplayName("@SpringJUnit5Config Tests") +class Spring5JUnit5ComposedAnnotationTests { + + @Autowired + Task task; + + @Autowired + List tasks; + + @Test + @DisplayName("ApplicationContext injected into method") + void givenAMethodName_whenInjecting_thenApplicationContextInjectedIntoMethod(ApplicationContext applicationContext) { + assertNotNull(applicationContext, "ApplicationContext should have been injected into method by Spring"); + assertEquals(this.task, applicationContext.getBean("taskName", Task.class)); + } + + @Test + @DisplayName("Spring @Beans injected into fields") + void givenAnObject_whenInjecting_thenSpringBeansInjected() { + assertNotNull(task, "Task should have been @Autowired by Spring"); + assertEquals("taskName", task.getName(), "Task's name"); + assertEquals(1, tasks.size(), "Number of Tasks in context"); + } +} diff --git a/spring-5/src/test/java/com/baeldung/jupiter/Spring5JUnit5ParallelTest.java b/spring-5/src/test/java/com/baeldung/jupiter/Spring5JUnit5ParallelTest.java new file mode 100644 index 000000000000..a060b78c93dc --- /dev/null +++ b/spring-5/src/test/java/com/baeldung/jupiter/Spring5JUnit5ParallelTest.java @@ -0,0 +1,29 @@ +package com.baeldung.jupiter; + +import com.baeldung.IntegrationTestExample1; +import com.baeldung.IntegrationTestExample2; +import org.junit.experimental.ParallelComputer; +import org.junit.jupiter.api.Test; +import org.junit.runner.Computer; +import org.junit.runner.JUnitCore; + +public class Spring5JUnit5ParallelTest { + + @Test + public void givenTwoTestClasses_whenJUnitRunParallel_thenTheTestsExecutingParallel() { + final Class[] classes = { + IntegrationTestExample1.class, IntegrationTestExample2.class + }; + + JUnitCore.runClasses(new ParallelComputer(true, true), classes); + } + + @Test + public void givenTwoTestClasses_whenJUnitRunParallel_thenTheTestsExecutingLinear() { + final Class[] classes = { + IntegrationTestExample1.class, IntegrationTestExample2.class + }; + + JUnitCore.runClasses(new Computer(), classes); + } +} \ No newline at end of file diff --git a/spring-5/src/test/java/com/baeldung/jupiter/Spring5JUnit5Tests.java b/spring-5/src/test/java/com/baeldung/jupiter/Spring5JUnit5Tests.java new file mode 100644 index 000000000000..c4c3148b1e6f --- /dev/null +++ b/spring-5/src/test/java/com/baeldung/jupiter/Spring5JUnit5Tests.java @@ -0,0 +1,27 @@ +package com.baeldung.jupiter; + +import com.baeldung.web.reactive.Task; + +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.test.context.ContextConfiguration; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.junit.jupiter.api.Test; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = TestConfig.class) +class Spring5JUnit5Tests { + + @Autowired + Task task; + + @Test + void givenAMethodName_whenInjecting_thenApplicationContextInjectedIntoMetho(ApplicationContext applicationContext) { + assertNotNull(applicationContext, "ApplicationContext should have been injected by Spring"); + assertEquals(this.task, applicationContext.getBean("taskName", Task.class)); + } +} diff --git a/spring-5/src/test/java/com/baeldung/jupiter/Spring5Java8NewFeaturesTest.java b/spring-5/src/test/java/com/baeldung/jupiter/Spring5Java8NewFeaturesTest.java new file mode 100644 index 000000000000..36adf1f7ff97 --- /dev/null +++ b/spring-5/src/test/java/com/baeldung/jupiter/Spring5Java8NewFeaturesTest.java @@ -0,0 +1,33 @@ +package com.baeldung.jupiter; + +import org.junit.jupiter.api.Test; +import java.util.function.Supplier; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class Spring5Java8NewFeaturesTest { + + @FunctionalInterface + public interface FunctionalInterfaceExample { + Result reverseString(Input input); + } + + public class StringUtils{ + public FunctionalInterfaceExample + functionLambdaString = s -> { + return Pattern.compile(" +").splitAsStream(s) + .map(word->new StringBuilder(word).reverse()) + .collect(Collectors.joining(" ")); + }; + } + + @Test + void givenStringUtil_whenSupplierCall_thenFunctionalInterfaceReverseString() + throws Exception { + Supplier stringUtilsSupplier = StringUtils::new; + + assertEquals(stringUtilsSupplier.get().functionLambdaString + .reverseString("hello"), "olleh"); + } +} diff --git a/spring-5/src/test/java/com/baeldung/jupiter/Spring5ReactiveServerClientTest.java b/spring-5/src/test/java/com/baeldung/jupiter/Spring5ReactiveServerClientTest.java new file mode 100644 index 000000000000..2252c255eeb7 --- /dev/null +++ b/spring-5/src/test/java/com/baeldung/jupiter/Spring5ReactiveServerClientTest.java @@ -0,0 +1,110 @@ +package com.baeldung.jupiter; + +import com.baeldung.web.reactive.Task; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpMethod; +import org.springframework.http.client.reactive.ReactorClientHttpConnector; +import org.springframework.http.server.reactive.HttpHandler; +import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter; +import org.springframework.web.reactive.function.BodyInserters; +import org.springframework.web.reactive.function.client.ClientRequest; +import org.springframework.web.reactive.function.client.ExchangeFunction; +import org.springframework.web.reactive.function.client.ExchangeFunctions; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.ipc.netty.NettyContext; +import reactor.ipc.netty.http.server.HttpServer; + +import java.net.URI; +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.web.reactive.function.server.RequestPredicates.GET; +import static org.springframework.web.reactive.function.server.RequestPredicates.POST; + +public class Spring5ReactiveServerClientTest { + + private static NettyContext nettyContext; + + @BeforeAll + public static void setUp() throws Exception { + HttpServer server = HttpServer.create("localhost", 8080); + RouterFunction route = RouterFunctions + .route(POST("/task/process"), request -> ServerResponse + .ok() + .body(request + .bodyToFlux(Task.class) + .map(ll -> new Task("TaskName", 1)), Task.class)) + .and(RouterFunctions.route(GET("/task"), request -> ServerResponse + .ok() + .body(Mono.just("server is alive"), String.class))); + HttpHandler httpHandler = RouterFunctions.toHttpHandler(route); + ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler); + nettyContext = server + .newHandler(adapter) + .block(); + } + + @AfterAll + public static void shutDown() { + nettyContext.dispose(); + } + + @Test + public void givenCheckTask_whenServerHandle_thenServerResponseALiveString() throws Exception { + WebClient client = WebClient.create("http://localhost:8080"); + Mono result = client + .get() + .uri("/task") + .exchange() + .then(response -> response.bodyToMono(String.class)); + + assertThat(result.block()).isInstanceOf(String.class); + } + + @Test + public void givenThreeTasks_whenServerHandleTheTasks_thenServerResponseATask() throws Exception { + URI uri = URI.create("http://localhost:8080/task/process"); + ExchangeFunction exchange = ExchangeFunctions.create(new ReactorClientHttpConnector()); + ClientRequest request = ClientRequest + .method(HttpMethod.POST, uri) + .body(BodyInserters.fromPublisher(getLatLngs(), Task.class)) + .build(); + + Flux taskResponse = exchange + .exchange(request) + .flatMap(response -> response.bodyToFlux(Task.class)); + + assertThat(taskResponse.blockFirst()).isInstanceOf(Task.class); + } + + @Test + public void givenCheckTask_whenServerHandle_thenOragicServerResponseALiveString() throws Exception { + URI uri = URI.create("http://localhost:8080/task"); + ExchangeFunction exchange = ExchangeFunctions.create(new ReactorClientHttpConnector()); + ClientRequest request = ClientRequest + .method(HttpMethod.GET, uri) + .body(BodyInserters.fromPublisher(getLatLngs(), Task.class)) + .build(); + + Flux taskResponse = exchange + .exchange(request) + .flatMap(response -> response.bodyToFlux(String.class)); + + assertThat(taskResponse.blockFirst()).isInstanceOf(String.class); + } + + private static Flux getLatLngs() { + return Flux + .range(0, 3) + .zipWith(Flux.interval(Duration.ofSeconds(1))) + .map(x -> new Task("taskname", 1)) + .doOnNext(ll -> System.out.println("Produced: {}" + ll)); + } +} diff --git a/spring-5/src/test/resources/baeldung-weekly.png b/spring-5/src/test/resources/baeldung-weekly.png new file mode 100644 index 000000000000..5a27d61dae71 Binary files /dev/null and b/spring-5/src/test/resources/baeldung-weekly.png differ diff --git a/spring-akka/pom.xml b/spring-akka/pom.xml index 1a273b0fedff..497022064c4d 100644 --- a/spring-akka/pom.xml +++ b/spring-akka/pom.xml @@ -12,6 +12,12 @@ org.springframework spring-context + + + commons-logging + commons-logging + + @@ -70,6 +76,8 @@ maven-surefire-plugin ${maven-surefire-plugin.version} + 3 + true **/*IntegrationTest.java **/*LiveTest.java diff --git a/spring-all/README.md b/spring-all/README.md index a8ea7c58c765..29be01c79e73 100644 --- a/spring-all/README.md +++ b/spring-all/README.md @@ -8,6 +8,7 @@ This project is used to replicate Spring Exceptions only. The "REST With Spring" Classes: http://bit.ly/restwithspring ### Relevant articles: +- [Guide to Spring @Autowired](http://www.baeldung.com/spring-autowire) - [Properties with Spring](http://www.baeldung.com/2012/02/06/properties-with-spring) - checkout the `org.baeldung.properties` package for all scenarios of properties injection and usage - [Spring Profiles](http://www.baeldung.com/spring-profiles) - [A Spring Custom Annotation for a Better DAO](http://www.baeldung.com/spring-annotation-bean-pre-processor) @@ -18,3 +19,7 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring - [Introduction To Ehcache](http://www.baeldung.com/ehcache) - [A Guide to the Spring Task Scheduler](http://www.baeldung.com/spring-task-scheduler) - [Guide to Spring Retry](http://www.baeldung.com/spring-retry) +- [Custom Scope in Spring](http://www.baeldung.com/spring-custom-scope) +- [New in Guava 21 common.util.concurrent](http://www.baeldung.com/guava-21-util-concurrent) +- [A CLI with Spring Shell](http://www.baeldung.com/spring-shell-cli) +- [JasperReports with Spring](http://www.baeldung.com/spring-jasper) diff --git a/spring-all/pom.xml b/spring-all/pom.xml index f28fe1f10dcc..7fbb6acced5a 100644 --- a/spring-all/pom.xml +++ b/spring-all/pom.xml @@ -25,6 +25,12 @@ org.springframework spring-web + + + commons-logging + commons-logging + + org.springframework @@ -43,6 +49,12 @@ spring-retry ${springretry.version} + + org.springframework.shell + spring-shell + ${org.springframework.shell.version} + + @@ -100,6 +112,18 @@ guava ${guava.version} + + + net.sf.jasperreports + jasperreports + ${jasperreports.version} + + + commons-logging + commons-logging + + + @@ -117,10 +141,6 @@ jcl-over-slf4j - - org.slf4j - log4j-over-slf4j - @@ -280,6 +300,7 @@ 4.3.4.RELEASE 4.2.0.RELEASE 1.1.5.RELEASE + 1.2.0.RELEASE 5.2.5.Final @@ -289,6 +310,7 @@ 3.1.3 3.4 3.6.1 + 6.4.0 diff --git a/spring-all/src/main/java/org/baeldung/customscope/TenantBean.java b/spring-all/src/main/java/org/baeldung/customscope/TenantBean.java new file mode 100644 index 000000000000..2d3049ebb991 --- /dev/null +++ b/spring-all/src/main/java/org/baeldung/customscope/TenantBean.java @@ -0,0 +1,16 @@ +package org.baeldung.customscope; + +public class TenantBean { + + private final String name; + + public TenantBean(String name) { + this.name = name; + } + + public void sayHello() { + System.out.println(String.format("Hello from %s of type %s", + this.name, + this.getClass().getName())); + } +} diff --git a/spring-all/src/main/java/org/baeldung/customscope/TenantBeanFactoryPostProcessor.java b/spring-all/src/main/java/org/baeldung/customscope/TenantBeanFactoryPostProcessor.java new file mode 100644 index 000000000000..2757399fa627 --- /dev/null +++ b/spring-all/src/main/java/org/baeldung/customscope/TenantBeanFactoryPostProcessor.java @@ -0,0 +1,13 @@ +package org.baeldung.customscope; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; + +public class TenantBeanFactoryPostProcessor implements BeanFactoryPostProcessor { + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) throws BeansException { + factory.registerScope("tenant", new TenantScope()); + } +} diff --git a/spring-all/src/main/java/org/baeldung/customscope/TenantBeansConfig.java b/spring-all/src/main/java/org/baeldung/customscope/TenantBeansConfig.java new file mode 100644 index 000000000000..0e21ad93446c --- /dev/null +++ b/spring-all/src/main/java/org/baeldung/customscope/TenantBeansConfig.java @@ -0,0 +1,21 @@ +package org.baeldung.customscope; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Scope; + +@Configuration +public class TenantBeansConfig { + + @Scope(scopeName = "tenant") + @Bean + public TenantBean foo() { + return new TenantBean("foo"); + } + + @Scope(scopeName = "tenant") + @Bean + public TenantBean bar() { + return new TenantBean("bar"); + } +} diff --git a/spring-all/src/main/java/org/baeldung/customscope/TenantScope.java b/spring-all/src/main/java/org/baeldung/customscope/TenantScope.java new file mode 100644 index 000000000000..062d7ee890ba --- /dev/null +++ b/spring-all/src/main/java/org/baeldung/customscope/TenantScope.java @@ -0,0 +1,43 @@ +package org.baeldung.customscope; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.beans.factory.ObjectFactory; +import org.springframework.beans.factory.config.Scope; + +public class TenantScope implements Scope { + + private Map scopedObjects = Collections.synchronizedMap(new HashMap()); + private Map destructionCallbacks = Collections.synchronizedMap(new HashMap()); + + @Override + public Object get(String name, ObjectFactory objectFactory) { + if(!scopedObjects.containsKey(name)) { + scopedObjects.put(name, objectFactory.getObject()); + } + return scopedObjects.get(name); + } + + @Override + public Object remove(String name) { + destructionCallbacks.remove(name); + return scopedObjects.remove(name); + } + + @Override + public void registerDestructionCallback(String name, Runnable callback) { + destructionCallbacks.put(name, callback); + } + + @Override + public Object resolveContextualObject(String key) { + return null; + } + + @Override + public String getConversationId() { + return "tenant"; + } +} diff --git a/spring-all/src/main/java/org/baeldung/customscope/TenantScopeConfig.java b/spring-all/src/main/java/org/baeldung/customscope/TenantScopeConfig.java new file mode 100644 index 000000000000..d1159b0f60ae --- /dev/null +++ b/spring-all/src/main/java/org/baeldung/customscope/TenantScopeConfig.java @@ -0,0 +1,14 @@ +package org.baeldung.customscope; + +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class TenantScopeConfig { + + @Bean + public static BeanFactoryPostProcessor beanFactoryPostProcessor() { + return new TenantBeanFactoryPostProcessor(); + } +} diff --git a/spring-all/src/main/java/org/baeldung/jasperreports/Main.java b/spring-all/src/main/java/org/baeldung/jasperreports/Main.java new file mode 100644 index 000000000000..79aee100a6ac --- /dev/null +++ b/spring-all/src/main/java/org/baeldung/jasperreports/Main.java @@ -0,0 +1,39 @@ +package org.baeldung.jasperreports; + +import java.util.HashMap; +import java.util.Map; +import org.baeldung.jasperreports.config.JasperRerportsSimpleConfig; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +public class Main { + public static void main(String[] args) { + + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(JasperRerportsSimpleConfig.class); + ctx.refresh(); + + SimpleReportFiller simpleReportFiller = ctx.getBean(SimpleReportFiller.class); + simpleReportFiller.setReportFileName("employeeEmailReport.jrxml"); + simpleReportFiller.compileReport(); + + simpleReportFiller.setReportFileName("employeeReport.jrxml"); + simpleReportFiller.compileReport(); + + Map parameters = new HashMap<>(); + parameters.put("title", "Employee Report Example"); + parameters.put("minSalary", 15000.0); + parameters.put("condition", " LAST_NAME ='Smith' ORDER BY FIRST_NAME"); + + simpleReportFiller.setParameters(parameters); + simpleReportFiller.fillReport(); + + SimpleReportExporter simpleExporter = ctx.getBean(SimpleReportExporter.class); + simpleExporter.setJasperPrint(simpleReportFiller.getJasperPrint()); + + simpleExporter.exportToPdf("employeeReport.pdf", "baeldung"); + simpleExporter.exportToXlsx("employeeReport.xlsx", "Employee Data"); + simpleExporter.exportToCsv("employeeReport.csv"); + simpleExporter.exportToHtml("employeeReport.html"); + + } +} diff --git a/spring-all/src/main/java/org/baeldung/jasperreports/SimpleReportExporter.java b/spring-all/src/main/java/org/baeldung/jasperreports/SimpleReportExporter.java new file mode 100644 index 000000000000..1252dcda4da9 --- /dev/null +++ b/spring-all/src/main/java/org/baeldung/jasperreports/SimpleReportExporter.java @@ -0,0 +1,115 @@ +package org.baeldung.jasperreports; + +import java.util.logging.Level; +import java.util.logging.Logger; +import net.sf.jasperreports.engine.JRException; +import net.sf.jasperreports.engine.JasperPrint; +import net.sf.jasperreports.engine.export.HtmlExporter; +import net.sf.jasperreports.engine.export.JRCsvExporter; +import net.sf.jasperreports.engine.export.JRPdfExporter; +import net.sf.jasperreports.engine.export.ooxml.JRXlsxExporter; +import net.sf.jasperreports.export.SimpleExporterInput; +import net.sf.jasperreports.export.SimpleHtmlExporterOutput; +import net.sf.jasperreports.export.SimpleOutputStreamExporterOutput; +import net.sf.jasperreports.export.SimplePdfExporterConfiguration; +import net.sf.jasperreports.export.SimplePdfReportConfiguration; +import net.sf.jasperreports.export.SimpleWriterExporterOutput; +import net.sf.jasperreports.export.SimpleXlsxReportConfiguration; +import org.springframework.stereotype.Component; + +@Component +public class SimpleReportExporter { + + private JasperPrint jasperPrint; + + public SimpleReportExporter() { + } + + public SimpleReportExporter (JasperPrint jasperPrint){ + this.jasperPrint = jasperPrint; + } + + public JasperPrint getJasperPrint() { + return jasperPrint; + } + + public void setJasperPrint(JasperPrint jasperPrint) { + this.jasperPrint = jasperPrint; + } + + + + public void exportToPdf(String fileName, String author) { + + // print report to file + JRPdfExporter exporter = new JRPdfExporter(); + + exporter.setExporterInput(new SimpleExporterInput(jasperPrint)); + exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(fileName)); + + SimplePdfReportConfiguration reportConfig = new SimplePdfReportConfiguration(); + reportConfig.setSizePageToContent(true); + reportConfig.setForceLineBreakPolicy(false); + + SimplePdfExporterConfiguration exportConfig = new SimplePdfExporterConfiguration(); + exportConfig.setMetadataAuthor(author); + exportConfig.setEncrypted(true); + exportConfig.setAllowedPermissionsHint("PRINTING"); + + exporter.setConfiguration(reportConfig); + exporter.setConfiguration(exportConfig); + try { + exporter.exportReport(); + } catch (JRException ex) { + Logger.getLogger(SimpleReportFiller.class.getName()) + .log(Level.SEVERE, null, ex); + } + } + + public void exportToXlsx(String fileName,String sheetName) { + JRXlsxExporter exporter = new JRXlsxExporter(); + + exporter.setExporterInput(new SimpleExporterInput(jasperPrint)); + exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(fileName)); + + SimpleXlsxReportConfiguration reportConfig = new SimpleXlsxReportConfiguration(); + reportConfig.setSheetNames(new String[]{sheetName}); + + exporter.setConfiguration(reportConfig); + + try { + exporter.exportReport(); + } catch (JRException ex) { + Logger.getLogger(SimpleReportFiller.class.getName()) + .log(Level.SEVERE, null, ex); + } + } + + public void exportToCsv(String fileName) { + JRCsvExporter exporter = new JRCsvExporter(); + + exporter.setExporterInput(new SimpleExporterInput(jasperPrint)); + exporter.setExporterOutput(new SimpleWriterExporterOutput(fileName)); + + try { + exporter.exportReport(); + } catch (JRException ex) { + Logger.getLogger(SimpleReportFiller.class.getName()) + .log(Level.SEVERE, null, ex); + } + } + + public void exportToHtml(String fileName) { + HtmlExporter exporter = new HtmlExporter(); + + exporter.setExporterInput(new SimpleExporterInput(jasperPrint)); + exporter.setExporterOutput(new SimpleHtmlExporterOutput(fileName)); + + try { + exporter.exportReport(); + } catch (JRException ex) { + Logger.getLogger(SimpleReportFiller.class.getName()) + .log(Level.SEVERE, null, ex); + } + } +} diff --git a/spring-all/src/main/java/org/baeldung/jasperreports/SimpleReportFiller.java b/spring-all/src/main/java/org/baeldung/jasperreports/SimpleReportFiller.java new file mode 100644 index 000000000000..ae675dd28094 --- /dev/null +++ b/spring-all/src/main/java/org/baeldung/jasperreports/SimpleReportFiller.java @@ -0,0 +1,90 @@ +package org.baeldung.jasperreports; + +import java.io.InputStream; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.sql.DataSource; +import net.sf.jasperreports.engine.JRException; +import net.sf.jasperreports.engine.JasperCompileManager; +import net.sf.jasperreports.engine.JasperFillManager; +import net.sf.jasperreports.engine.JasperPrint; +import net.sf.jasperreports.engine.JasperReport; +import net.sf.jasperreports.engine.util.JRSaver; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class SimpleReportFiller { + + private String reportFileName; + + private JasperReport jasperReport; + + private JasperPrint jasperPrint; + + @Autowired + private DataSource dataSource; + + private Map parameters; + + public SimpleReportFiller() { + parameters = new HashMap<>(); + } + + public void prepareReport() { + compileReport(); + fillReport(); + } + + public void compileReport() { + try { + InputStream reportStream = getClass().getResourceAsStream("/".concat(reportFileName)); + jasperReport = JasperCompileManager.compileReport(reportStream); + JRSaver.saveObject(jasperReport, reportFileName.replace(".jrxml", ".jasper")); + } catch (JRException ex) { + Logger.getLogger(SimpleReportFiller.class.getName()) + .log(Level.SEVERE, null, ex); + } + } + + public void fillReport() { + try { + jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, dataSource.getConnection()); + } catch (JRException | SQLException ex) { + Logger.getLogger(SimpleReportFiller.class.getName()) + .log(Level.SEVERE, null, ex); + } + } + + public DataSource getDataSource() { + return dataSource; + } + + public void setDataSource(DataSource dataSource) { + this.dataSource = dataSource; + } + + public Map getParameters() { + return parameters; + } + + public void setParameters(Map parameters) { + this.parameters = parameters; + } + + public String getReportFileName() { + return reportFileName; + } + + public void setReportFileName(String reportFileName) { + this.reportFileName = reportFileName; + } + + public JasperPrint getJasperPrint() { + return jasperPrint; + } + +} diff --git a/spring-all/src/main/java/org/baeldung/jasperreports/config/JasperRerportsSimpleConfig.java b/spring-all/src/main/java/org/baeldung/jasperreports/config/JasperRerportsSimpleConfig.java new file mode 100644 index 000000000000..90fc96cdbb9e --- /dev/null +++ b/spring-all/src/main/java/org/baeldung/jasperreports/config/JasperRerportsSimpleConfig.java @@ -0,0 +1,32 @@ +package org.baeldung.jasperreports.config; + +import javax.sql.DataSource; +import org.baeldung.jasperreports.SimpleReportExporter; +import org.baeldung.jasperreports.SimpleReportFiller; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; + +@Configuration +public class JasperRerportsSimpleConfig { + + @Bean + public DataSource dataSource() { + return new EmbeddedDatabaseBuilder() + .setType(EmbeddedDatabaseType.HSQL) + .addScript("classpath:employee-schema.sql") + .build(); + } + + @Bean + public SimpleReportFiller reportFiller() { + return new SimpleReportFiller(); + } + + @Bean + public SimpleReportExporter reportExporter() { + return new SimpleReportExporter(); + } + +} diff --git a/spring-autowire/src/main/java/com/baeldung/autowire/sample/App.java b/spring-all/src/main/java/org/baeldung/sample/App.java similarity index 90% rename from spring-autowire/src/main/java/com/baeldung/autowire/sample/App.java rename to spring-all/src/main/java/org/baeldung/sample/App.java index 18ff11a49c65..17fc49fc8ce5 100644 --- a/spring-autowire/src/main/java/com/baeldung/autowire/sample/App.java +++ b/spring-all/src/main/java/org/baeldung/sample/App.java @@ -1,4 +1,4 @@ -package com.baeldung.autowire.sample; +package org.baeldung.sample; import org.springframework.context.annotation.AnnotationConfigApplicationContext; diff --git a/spring-autowire/src/main/java/com/baeldung/autowire/sample/AppConfig.java b/spring-all/src/main/java/org/baeldung/sample/AppConfig.java similarity index 66% rename from spring-autowire/src/main/java/com/baeldung/autowire/sample/AppConfig.java rename to spring-all/src/main/java/org/baeldung/sample/AppConfig.java index f948e2bf8ef0..8a177d2611e1 100644 --- a/spring-autowire/src/main/java/com/baeldung/autowire/sample/AppConfig.java +++ b/spring-all/src/main/java/org/baeldung/sample/AppConfig.java @@ -1,10 +1,10 @@ -package com.baeldung.autowire.sample; +package org.baeldung.sample; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration -@ComponentScan("com.baeldung.autowire.sample") +@ComponentScan("org.baeldung.sample") public class AppConfig { } diff --git a/spring-all/src/main/java/org/baeldung/sample/Bar.java b/spring-all/src/main/java/org/baeldung/sample/Bar.java new file mode 100644 index 000000000000..54e8d544180b --- /dev/null +++ b/spring-all/src/main/java/org/baeldung/sample/Bar.java @@ -0,0 +1,5 @@ +package org.baeldung.sample; + +public class Bar { + +} diff --git a/spring-autowire/src/main/java/com/baeldung/autowire/sample/BarFormatter.java b/spring-all/src/main/java/org/baeldung/sample/BarFormatter.java similarity index 83% rename from spring-autowire/src/main/java/com/baeldung/autowire/sample/BarFormatter.java rename to spring-all/src/main/java/org/baeldung/sample/BarFormatter.java index e67a376d2540..8396653970f1 100644 --- a/spring-autowire/src/main/java/com/baeldung/autowire/sample/BarFormatter.java +++ b/spring-all/src/main/java/org/baeldung/sample/BarFormatter.java @@ -1,4 +1,4 @@ -package com.baeldung.autowire.sample; +package org.baeldung.sample; import org.springframework.stereotype.Component; diff --git a/spring-all/src/main/java/org/baeldung/sample/Foo.java b/spring-all/src/main/java/org/baeldung/sample/Foo.java new file mode 100644 index 000000000000..562a00121a70 --- /dev/null +++ b/spring-all/src/main/java/org/baeldung/sample/Foo.java @@ -0,0 +1,5 @@ +package org.baeldung.sample; + +public class Foo { + +} diff --git a/spring-all/src/main/java/org/baeldung/sample/FooDAO.java b/spring-all/src/main/java/org/baeldung/sample/FooDAO.java new file mode 100644 index 000000000000..151c0c38defc --- /dev/null +++ b/spring-all/src/main/java/org/baeldung/sample/FooDAO.java @@ -0,0 +1,5 @@ +package org.baeldung.sample; + +public class FooDAO { + +} diff --git a/spring-autowire/src/main/java/com/baeldung/autowire/sample/FooFormatter.java b/spring-all/src/main/java/org/baeldung/sample/FooFormatter.java similarity index 83% rename from spring-autowire/src/main/java/com/baeldung/autowire/sample/FooFormatter.java rename to spring-all/src/main/java/org/baeldung/sample/FooFormatter.java index 57f93a53d768..68cb7f81f2da 100644 --- a/spring-autowire/src/main/java/com/baeldung/autowire/sample/FooFormatter.java +++ b/spring-all/src/main/java/org/baeldung/sample/FooFormatter.java @@ -1,4 +1,4 @@ -package com.baeldung.autowire.sample; +package org.baeldung.sample; import org.springframework.stereotype.Component; diff --git a/spring-autowire/src/main/java/com/baeldung/autowire/sample/FooService.java b/spring-all/src/main/java/org/baeldung/sample/FooService.java similarity index 88% rename from spring-autowire/src/main/java/com/baeldung/autowire/sample/FooService.java rename to spring-all/src/main/java/org/baeldung/sample/FooService.java index c55d93da11d5..711711f2053b 100644 --- a/spring-autowire/src/main/java/com/baeldung/autowire/sample/FooService.java +++ b/spring-all/src/main/java/org/baeldung/sample/FooService.java @@ -1,4 +1,4 @@ -package com.baeldung.autowire.sample; +package org.baeldung.sample; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; diff --git a/spring-autowire/src/main/java/com/baeldung/autowire/sample/Formatter.java b/spring-all/src/main/java/org/baeldung/sample/Formatter.java similarity index 59% rename from spring-autowire/src/main/java/com/baeldung/autowire/sample/Formatter.java rename to spring-all/src/main/java/org/baeldung/sample/Formatter.java index 59d718050a5a..ab29c2b8486a 100644 --- a/spring-autowire/src/main/java/com/baeldung/autowire/sample/Formatter.java +++ b/spring-all/src/main/java/org/baeldung/sample/Formatter.java @@ -1,4 +1,4 @@ -package com.baeldung.autowire.sample; +package org.baeldung.sample; public interface Formatter { diff --git a/spring-autowire/src/main/java/com/baeldung/autowire/sample/FormatterType.java b/spring-all/src/main/java/org/baeldung/sample/FormatterType.java similarity index 91% rename from spring-autowire/src/main/java/com/baeldung/autowire/sample/FormatterType.java rename to spring-all/src/main/java/org/baeldung/sample/FormatterType.java index f2961745b54b..d4d82dd0229b 100644 --- a/spring-autowire/src/main/java/com/baeldung/autowire/sample/FormatterType.java +++ b/spring-all/src/main/java/org/baeldung/sample/FormatterType.java @@ -1,4 +1,4 @@ -package com.baeldung.autowire.sample; +package org.baeldung.sample; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/spring-all/src/main/java/org/baeldung/scopes/ScopesController.java b/spring-all/src/main/java/org/baeldung/scopes/ScopesController.java index 1e3d5f3b1476..73df386aff00 100644 --- a/spring-all/src/main/java/org/baeldung/scopes/ScopesController.java +++ b/spring-all/src/main/java/org/baeldung/scopes/ScopesController.java @@ -2,14 +2,16 @@ import javax.annotation.Resource; -import org.apache.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class ScopesController { - public static final Logger LOG = Logger.getLogger(ScopesController.class); + public static final Logger LOG = LoggerFactory.getLogger(ScopesController.class); @Resource(name = "requestMessage") HelloMessageGenerator requestMessage; diff --git a/spring-all/src/main/java/org/baeldung/shell/Main.java b/spring-all/src/main/java/org/baeldung/shell/Main.java new file mode 100644 index 000000000000..3d9f7a58608b --- /dev/null +++ b/spring-all/src/main/java/org/baeldung/shell/Main.java @@ -0,0 +1,10 @@ +package org.baeldung.shell; + +import java.io.IOException; +import org.springframework.shell.Bootstrap; + +public class Main { + public static void main(String[] args) throws IOException { + Bootstrap.main(args); + } +} diff --git a/spring-all/src/main/java/org/baeldung/shell/simple/SimpleBannerProvider.java b/spring-all/src/main/java/org/baeldung/shell/simple/SimpleBannerProvider.java new file mode 100644 index 000000000000..df7a48cd3242 --- /dev/null +++ b/spring-all/src/main/java/org/baeldung/shell/simple/SimpleBannerProvider.java @@ -0,0 +1,34 @@ +package org.baeldung.shell.simple; + +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.shell.plugin.support.DefaultBannerProvider; +import org.springframework.shell.support.util.OsUtils; +import org.springframework.stereotype.Component; + +@Component +@Order(Ordered.HIGHEST_PRECEDENCE) +public class SimpleBannerProvider extends DefaultBannerProvider { + + public String getBanner() { + StringBuffer buf = new StringBuffer(); + buf.append("=======================================").append(OsUtils.LINE_SEPARATOR); + buf.append("* Baeldung Shell *").append(OsUtils.LINE_SEPARATOR); + buf.append("=======================================").append(OsUtils.LINE_SEPARATOR); + buf.append("Version:").append(this.getVersion()); + return buf.toString(); + } + + public String getVersion() { + return "1.0.1"; + } + + public String getWelcomeMessage() { + return "Welcome to Baeldung CLI"; + } + + @Override + public String getProviderName() { + return "Baeldung Banner"; + } +} diff --git a/spring-all/src/main/java/org/baeldung/shell/simple/SimpleCLI.java b/spring-all/src/main/java/org/baeldung/shell/simple/SimpleCLI.java new file mode 100644 index 000000000000..0bbc62cf2c41 --- /dev/null +++ b/spring-all/src/main/java/org/baeldung/shell/simple/SimpleCLI.java @@ -0,0 +1,81 @@ +package org.baeldung.shell.simple; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.URL; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.springframework.shell.Bootstrap; +import org.springframework.shell.core.CommandMarker; +import org.springframework.shell.core.annotation.CliAvailabilityIndicator; +import org.springframework.shell.core.annotation.CliCommand; +import org.springframework.shell.core.annotation.CliOption; +import org.springframework.stereotype.Component; + +@Component +public class SimpleCLI implements CommandMarker { + + private String getContentsOfUrlAsString(URL url) { + StringBuilder sb = new StringBuilder(); + try { + try (BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()))) { + String inputLine; + while ((inputLine = in.readLine()) != null) { + sb.append(inputLine); + } + } + } catch (IOException ex) { + sb.append("ERROR"); + } + return sb.toString(); + } + + @CliCommand( + value = {"web-get", "wg"}, + help = "Displays the contents of a URL." + ) + public String webGet( + @CliOption( + key = {"", "url"}, + help = "URL whose contents will be displayed." + ) URL url) { + return getContentsOfUrlAsString(url); + } + + @CliCommand( + value = {"web-save", "ws"}, + help = "Saves the contents of a URL.") + public String webSave( + @CliOption(key = {"", "url"}, help = "URL whose contents will be saved.") URL url, + @CliOption(key = {"out", "file"}, mandatory = true, help = "The name of the file.") String file) { + String contents = getContentsOfUrlAsString(url); + try (PrintWriter out = new PrintWriter(file)) { + out.write(contents); + } catch (FileNotFoundException ex) { + //Ignore + } + return "Done."; + } + + private boolean adminEnableExecuted = false; + + @CliAvailabilityIndicator(value = {"web-save"}) + public boolean isAdminEnabled() { + return adminEnableExecuted; + } + + @CliCommand(value = "admin-enable") + public String adminEnable() { + adminEnableExecuted = true; + return "Admin commands enabled."; + } + + @CliCommand(value = "admin-disable") + public String adminDisable() { + adminEnableExecuted = false; + return "Admin commands disabled."; + } +} diff --git a/spring-all/src/main/java/org/baeldung/shell/simple/SimpleHistoryFileNameProvider.java b/spring-all/src/main/java/org/baeldung/shell/simple/SimpleHistoryFileNameProvider.java new file mode 100644 index 000000000000..cef53adc6992 --- /dev/null +++ b/spring-all/src/main/java/org/baeldung/shell/simple/SimpleHistoryFileNameProvider.java @@ -0,0 +1,22 @@ +package org.baeldung.shell.simple; + +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.shell.plugin.support.DefaultHistoryFileNameProvider; +import org.springframework.stereotype.Component; + +@Component +@Order(Ordered.HIGHEST_PRECEDENCE) +public class SimpleHistoryFileNameProvider extends DefaultHistoryFileNameProvider { + + @Override + public String getHistoryFileName() { + return "baeldung-shell.log"; + } + + @Override + public String getProviderName() { + return "Baeldung History"; + } + +} diff --git a/spring-all/src/main/java/org/baeldung/shell/simple/SimplePromptProvider.java b/spring-all/src/main/java/org/baeldung/shell/simple/SimplePromptProvider.java new file mode 100644 index 000000000000..9a84954e0574 --- /dev/null +++ b/spring-all/src/main/java/org/baeldung/shell/simple/SimplePromptProvider.java @@ -0,0 +1,21 @@ +package org.baeldung.shell.simple; + +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.shell.plugin.support.DefaultPromptProvider; +import org.springframework.stereotype.Component; + +@Component +@Order(Ordered.HIGHEST_PRECEDENCE) +public class SimplePromptProvider extends DefaultPromptProvider { + + @Override + public String getPrompt() { + return "baeldung-shell>"; + } + + @Override + public String getProviderName() { + return "Baeldung Prompt"; + } +} diff --git a/spring-all/src/main/java/org/baeldung/shell/simple/SimpleURLConverter.java b/spring-all/src/main/java/org/baeldung/shell/simple/SimpleURLConverter.java new file mode 100644 index 000000000000..66bab5c488b8 --- /dev/null +++ b/spring-all/src/main/java/org/baeldung/shell/simple/SimpleURLConverter.java @@ -0,0 +1,35 @@ +package org.baeldung.shell.simple; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.List; +import org.springframework.shell.core.Completion; +import org.springframework.shell.core.Converter; +import org.springframework.shell.core.MethodTarget; +import org.springframework.stereotype.Component; + +@Component +public class SimpleURLConverter implements Converter { + + @Override + public URL convertFromText(String value, Class requiredType, String optionContext) { + try { + return new URL(value); + } catch (MalformedURLException ex) { + //Ignore + } + return null; + } + + @Override + public boolean getAllPossibleValues(List completions, Class requiredType, + String existingData, String optionContext, MethodTarget target) { + return false; + } + + @Override + public boolean supports(Class requiredType, String optionContext) { + return URL.class.isAssignableFrom(requiredType); + } + +} diff --git a/spring-all/src/main/java/org/baeldung/startup/AllStrategiesExampleBean.java b/spring-all/src/main/java/org/baeldung/startup/AllStrategiesExampleBean.java index d4334437f7c8..f36b8703ea42 100644 --- a/spring-all/src/main/java/org/baeldung/startup/AllStrategiesExampleBean.java +++ b/spring-all/src/main/java/org/baeldung/startup/AllStrategiesExampleBean.java @@ -1,6 +1,8 @@ package org.baeldung.startup; -import org.apache.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import org.springframework.beans.factory.InitializingBean; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @@ -11,7 +13,7 @@ @Scope(value = "prototype") public class AllStrategiesExampleBean implements InitializingBean { - private static final Logger LOG = Logger.getLogger(AllStrategiesExampleBean.class); + private static final Logger LOG = LoggerFactory.getLogger(AllStrategiesExampleBean.class); public AllStrategiesExampleBean() { LOG.info("Constructor"); diff --git a/spring-all/src/main/java/org/baeldung/startup/EventListenerExampleBean.java b/spring-all/src/main/java/org/baeldung/startup/EventListenerExampleBean.java index e9cd1a159d20..58e42337cc84 100644 --- a/spring-all/src/main/java/org/baeldung/startup/EventListenerExampleBean.java +++ b/spring-all/src/main/java/org/baeldung/startup/EventListenerExampleBean.java @@ -1,13 +1,15 @@ package org.baeldung.startup; -import org.apache.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; @Component public class EventListenerExampleBean { - private static final Logger LOG = Logger.getLogger(EventListenerExampleBean.class); + private static final Logger LOG = LoggerFactory.getLogger(EventListenerExampleBean.class); public static int counter; diff --git a/spring-all/src/main/java/org/baeldung/startup/InitMethodExampleBean.java b/spring-all/src/main/java/org/baeldung/startup/InitMethodExampleBean.java index cea6b026d615..f3d30011f8ad 100644 --- a/spring-all/src/main/java/org/baeldung/startup/InitMethodExampleBean.java +++ b/spring-all/src/main/java/org/baeldung/startup/InitMethodExampleBean.java @@ -1,23 +1,24 @@ package org.baeldung.startup; -import org.apache.log4j.Logger; +import java.util.Arrays; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; -import java.util.Arrays; - @Component @Scope(value = "prototype") public class InitMethodExampleBean { - private static final Logger LOG = Logger.getLogger(InitMethodExampleBean.class); + private static final Logger LOG = LoggerFactory.getLogger(InitMethodExampleBean.class); @Autowired private Environment environment; public void init() { - LOG.info(Arrays.asList(environment.getDefaultProfiles())); + LOG.info("Env Default Profiles", Arrays.asList(environment.getDefaultProfiles())); } } diff --git a/spring-all/src/main/java/org/baeldung/startup/InitializingBeanExampleBean.java b/spring-all/src/main/java/org/baeldung/startup/InitializingBeanExampleBean.java index 33b14864f32e..5d6f8c9d88a8 100644 --- a/spring-all/src/main/java/org/baeldung/startup/InitializingBeanExampleBean.java +++ b/spring-all/src/main/java/org/baeldung/startup/InitializingBeanExampleBean.java @@ -1,25 +1,26 @@ package org.baeldung.startup; -import org.apache.log4j.Logger; +import java.util.Arrays; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; -import java.util.Arrays; - @Component @Scope(value = "prototype") public class InitializingBeanExampleBean implements InitializingBean { - private static final Logger LOG = Logger.getLogger(InitializingBeanExampleBean.class); + private static final Logger LOG = LoggerFactory.getLogger(InitializingBeanExampleBean.class); @Autowired private Environment environment; @Override public void afterPropertiesSet() throws Exception { - LOG.info(Arrays.asList(environment.getDefaultProfiles())); + LOG.info("Env Default Profiles", Arrays.asList(environment.getDefaultProfiles())); } } diff --git a/spring-all/src/main/java/org/baeldung/startup/LogicInConstructorExampleBean.java b/spring-all/src/main/java/org/baeldung/startup/LogicInConstructorExampleBean.java index 2a7b3e26c777..d25a83942a88 100644 --- a/spring-all/src/main/java/org/baeldung/startup/LogicInConstructorExampleBean.java +++ b/spring-all/src/main/java/org/baeldung/startup/LogicInConstructorExampleBean.java @@ -1,25 +1,22 @@ package org.baeldung.startup; -import org.apache.log4j.Logger; +import java.util.Arrays; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; -import java.util.Arrays; - @Component @Scope(value = "prototype") public class LogicInConstructorExampleBean { - private static final Logger LOG = Logger.getLogger(LogicInConstructorExampleBean.class); - - private final Environment environment; + private static final Logger LOG = LoggerFactory.getLogger(LogicInConstructorExampleBean.class); @Autowired public LogicInConstructorExampleBean(Environment environment) { - this.environment = environment; - - LOG.info(Arrays.asList(environment.getDefaultProfiles())); + LOG.info("Env Default Profiles", Arrays.asList(environment.getDefaultProfiles())); } } diff --git a/spring-all/src/main/java/org/baeldung/startup/PostConstructExampleBean.java b/spring-all/src/main/java/org/baeldung/startup/PostConstructExampleBean.java index 4cabaad4dfa2..2397c3a0a27c 100644 --- a/spring-all/src/main/java/org/baeldung/startup/PostConstructExampleBean.java +++ b/spring-all/src/main/java/org/baeldung/startup/PostConstructExampleBean.java @@ -1,25 +1,27 @@ package org.baeldung.startup; -import org.apache.log4j.Logger; +import java.util.Arrays; + +import javax.annotation.PostConstruct; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; -import javax.annotation.PostConstruct; -import java.util.Arrays; - @Component @Scope(value = "prototype") public class PostConstructExampleBean { - private static final Logger LOG = Logger.getLogger(PostConstructExampleBean.class); + private static final Logger LOG = LoggerFactory.getLogger(PostConstructExampleBean.class); @Autowired private Environment environment; @PostConstruct public void init() { - LOG.info(Arrays.asList(environment.getDefaultProfiles())); + LOG.info("Env Default Profiles", Arrays.asList(environment.getDefaultProfiles())); } } diff --git a/spring-all/src/main/java/org/baeldung/startup/StartupApplicationListenerExample.java b/spring-all/src/main/java/org/baeldung/startup/StartupApplicationListenerExample.java index 32a63f0c1a39..e02388dea3c1 100644 --- a/spring-all/src/main/java/org/baeldung/startup/StartupApplicationListenerExample.java +++ b/spring-all/src/main/java/org/baeldung/startup/StartupApplicationListenerExample.java @@ -1,6 +1,8 @@ package org.baeldung.startup; -import org.apache.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.stereotype.Component; @@ -8,7 +10,7 @@ @Component public class StartupApplicationListenerExample implements ApplicationListener { - private static final Logger LOG = Logger.getLogger(StartupApplicationListenerExample.class); + private static final Logger LOG = LoggerFactory.getLogger(StartupApplicationListenerExample.class); public static int counter; diff --git a/spring-all/src/main/resources/META-INF/spring/spring-shell-plugin.xml b/spring-all/src/main/resources/META-INF/spring/spring-shell-plugin.xml new file mode 100644 index 000000000000..aea1a663c11c --- /dev/null +++ b/spring-all/src/main/resources/META-INF/spring/spring-shell-plugin.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/spring-all/src/main/resources/employee-schema.sql b/spring-all/src/main/resources/employee-schema.sql new file mode 100644 index 000000000000..011ff162e15a --- /dev/null +++ b/spring-all/src/main/resources/employee-schema.sql @@ -0,0 +1,27 @@ +CREATE TABLE EMPLOYEE( + ID INT NOT NULL PRIMARY KEY, + FIRST_NAME VARCHAR(255), + LAST_NAME VARCHAR(255), + SALARY DOUBLE, +); + +CREATE TABLE EMAIL( + ID INT NOT NULL PRIMARY KEY, + ID_EMPLOYEE VARCHAR(255), + ADDRESS VARCHAR(255) +); + +INSERT INTO EMPLOYEE VALUES (1, 'John', 'Doe', 10000.10); +INSERT INTO EMPLOYEE VALUES (2, 'Kevin', 'Smith', 20000.20); +INSERT INTO EMPLOYEE VALUES (3, 'Kim', 'Smith', 30000.30); +INSERT INTO EMPLOYEE VALUES (4, 'Stephen', 'Torvalds', 40000.40); +INSERT INTO EMPLOYEE VALUES (5, 'Christian', 'Reynolds', 50000.50); + +INSERT INTO EMAIL VALUES (1, 1, 'john@baeldung.com'); +INSERT INTO EMAIL VALUES (2, 1, 'john@gmail.com'); +INSERT INTO EMAIL VALUES (3, 2, 'kevin@baeldung.com'); +INSERT INTO EMAIL VALUES (4, 3, 'kim@baeldung.com'); +INSERT INTO EMAIL VALUES (5, 3, 'kim@gmail.com'); +INSERT INTO EMAIL VALUES (6, 3, 'kim@outlook.com'); +INSERT INTO EMAIL VALUES (7, 4, 'stephen@baeldung.com'); +INSERT INTO EMAIL VALUES (8, 5, 'christian@gmail.com'); diff --git a/spring-all/src/main/resources/employeeEmailReport.jrxml b/spring-all/src/main/resources/employeeEmailReport.jrxml new file mode 100644 index 000000000000..ad4aaaac9080 --- /dev/null +++ b/spring-all/src/main/resources/employeeEmailReport.jrxml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/spring-all/src/main/resources/employeeReport.jrxml b/spring-all/src/main/resources/employeeReport.jrxml new file mode 100644 index 000000000000..3d2be226fb67 --- /dev/null +++ b/spring-all/src/main/resources/employeeReport.jrxml @@ -0,0 +1,51 @@ + + + + + + + + + = $P{minSalary} AND $P!{condition}]]> + + + + + + + <band height="20" splitType="Stretch"> + <textField> + <reportElement x="238" y="0" width="100" height="20"/> + <textElement/> + <textFieldExpression class="java.lang.String"><![CDATA[$P{title}]]></textFieldExpression> + </textField> + </band> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-all/src/main/resources/logback.xml b/spring-all/src/main/resources/logback.xml index 45c9697f776f..ec0dc2469ae0 100644 --- a/spring-all/src/main/resources/logback.xml +++ b/spring-all/src/main/resources/logback.xml @@ -1,22 +1,19 @@ + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - + + - - - - - - - - - - - + + + + + \ No newline at end of file diff --git a/spring-all/src/test/java/org/baeldung/customscope/TenantScopeTest.java b/spring-all/src/test/java/org/baeldung/customscope/TenantScopeTest.java new file mode 100644 index 000000000000..641e57bffd62 --- /dev/null +++ b/spring-all/src/test/java/org/baeldung/customscope/TenantScopeTest.java @@ -0,0 +1,74 @@ +package org.baeldung.customscope; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +import java.util.Map; + +import org.junit.Test; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +public class TenantScopeTest { + + @Test + public final void whenRegisterScopeAndBeans_thenContextContainsFooAndBar() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + try{ + ctx.register(TenantScopeConfig.class); + ctx.register(TenantBeansConfig.class); + ctx.refresh(); + + TenantBean foo = (TenantBean) ctx.getBean("foo", TenantBean.class); + foo.sayHello(); + TenantBean bar = (TenantBean) ctx.getBean("bar", TenantBean.class); + bar.sayHello(); + Map foos = ctx.getBeansOfType(TenantBean.class); + + assertThat(foo, not(equalTo(bar))); + assertThat(foos.size(), equalTo(2)); + assertTrue(foos.containsValue(foo)); + assertTrue(foos.containsValue(bar)); + + BeanDefinition fooDefinition = ctx.getBeanDefinition("foo"); + BeanDefinition barDefinition = ctx.getBeanDefinition("bar"); + + assertThat(fooDefinition.getScope(), equalTo("tenant")); + assertThat(barDefinition.getScope(), equalTo("tenant")); + } + finally { + ctx.close(); + } + } + + @Test + public final void whenComponentScan_thenContextContainsFooAndBar() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + try{ + ctx.scan("org.baeldung.customscope"); + ctx.refresh(); + + TenantBean foo = (TenantBean) ctx.getBean("foo", TenantBean.class); + foo.sayHello(); + TenantBean bar = (TenantBean) ctx.getBean("bar", TenantBean.class); + bar.sayHello(); + Map foos = ctx.getBeansOfType(TenantBean.class); + + assertThat(foo, not(equalTo(bar))); + assertThat(foos.size(), equalTo(2)); + assertTrue(foos.containsValue(foo)); + assertTrue(foos.containsValue(bar)); + + BeanDefinition fooDefinition = ctx.getBeanDefinition("foo"); + BeanDefinition barDefinition = ctx.getBeanDefinition("bar"); + + assertThat(fooDefinition.getScope(), equalTo("tenant")); + assertThat(barDefinition.getScope(), equalTo("tenant")); + } + finally { + ctx.close(); + } + } +} diff --git a/spring-autowire/src/test/java/com/baeldung/autowire/sample/FooServiceIntegrationTest.java b/spring-all/src/test/java/org/baeldung/sample/FooServiceIntegrationTest.java similarity index 94% rename from spring-autowire/src/test/java/com/baeldung/autowire/sample/FooServiceIntegrationTest.java rename to spring-all/src/test/java/org/baeldung/sample/FooServiceIntegrationTest.java index 34ba7902ca7b..6b518395a18c 100644 --- a/spring-autowire/src/test/java/com/baeldung/autowire/sample/FooServiceIntegrationTest.java +++ b/spring-all/src/test/java/org/baeldung/sample/FooServiceIntegrationTest.java @@ -1,4 +1,4 @@ -package com.baeldung.autowire.sample; +package org.baeldung.sample; import org.junit.Assert; import org.junit.Test; diff --git a/spring-all/src/test/java/org/baeldung/shell/simple/SimpleCLIUnitTest.java b/spring-all/src/test/java/org/baeldung/shell/simple/SimpleCLIUnitTest.java new file mode 100644 index 000000000000..0353083943dc --- /dev/null +++ b/spring-all/src/test/java/org/baeldung/shell/simple/SimpleCLIUnitTest.java @@ -0,0 +1,86 @@ +package org.baeldung.shell.simple; + +import java.io.File; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.springframework.shell.Bootstrap; +import org.springframework.shell.core.CommandResult; +import org.springframework.shell.core.JLineShellComponent; + +public class SimpleCLIUnitTest { + + static JLineShellComponent shell; + + @BeforeClass + public static void startUp() throws InterruptedException { + Bootstrap bootstrap = new Bootstrap(); + shell = bootstrap.getJLineShellComponent(); + } + + @AfterClass + public static void shutdown() { + shell.stop(); + } + + public static JLineShellComponent getShell() { + return shell; + } + + @Test + public void givenCommandConfig_whenExecutingWebGetCommand_thenCorrectResult() { + + CommandResult resultWebSave = shell.executeCommand("web-get --url https://www.google.com"); + + Assert.assertTrue(resultWebSave.isSuccess()); + } + + @Test + public void givenCommandConfig_whenExecutingWebSaveCommand_thenCorrectResult() { + + shell.executeCommand("admin-enable"); + CommandResult result = shell.executeCommand("web-save --url https://www.google.com --out contents.txt"); + + Assert.assertArrayEquals( + new boolean[]{ + result.isSuccess(), + new File("contents.txt").exists()}, + new boolean[]{true, true}); + } + + @Test + public void givenCommandConfig_whenAdminEnableCommandExecuted_thenCorrectAvailability() { + + CommandResult resultAdminDisable = shell.executeCommand("admin-disable"); + CommandResult resultWebSaveUnavailable = shell.executeCommand("web-save --url https://www.google.com --out contents.txt"); + CommandResult resultAdminEnable = shell.executeCommand("admin-enable"); + CommandResult resultWebSaveAvailable = shell.executeCommand("web-save --url https://www.google.com --out contents.txt"); + + Assert.assertArrayEquals( + new boolean[]{ + resultAdminDisable.isSuccess(), + resultWebSaveUnavailable.isSuccess(), + resultAdminEnable.isSuccess(), + resultWebSaveAvailable.isSuccess()}, + new boolean[]{true, false, true, true}); + } + + @Test + public void givenCommandConfig_whenWebSaveCommandExecutedNoOutArgument_thenError() { + + shell.executeCommand("admin-enable"); + CommandResult resultWebSave = shell.executeCommand("web-save --url https://www.google.com"); + + Assert.assertEquals(resultWebSave.isSuccess(), false); + } + + @Test + public void givenCommandConfig_whenExecutingWebGetCommandWithDefaultArgument_thenCorrectResult() { + + CommandResult result = shell.executeCommand("web-get https://www.google.com"); + + Assert.assertEquals(result.isSuccess(), true); + } + +} diff --git a/spring-amqp-simple/pom.xml b/spring-amqp-simple/pom.xml new file mode 100644 index 000000000000..7ff26376e455 --- /dev/null +++ b/spring-amqp-simple/pom.xml @@ -0,0 +1,56 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 1.5.2.RELEASE + + + com.baeldung + spring-amqp-simple + 1.0.0-SNAPSHOT + Spring AMQP Simple App + + + 1.8 + + + + + org.springframework.boot + spring-boot-starter-amqp + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*IntegrationTest.java + **/*LiveTest.java + + + + + + \ No newline at end of file diff --git a/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/MessageConsumer.java b/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/MessageConsumer.java new file mode 100644 index 000000000000..6eb13c18f68d --- /dev/null +++ b/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/MessageConsumer.java @@ -0,0 +1,17 @@ +package com.baeldung.springamqpsimple; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.stereotype.Component; + +@Component +public class MessageConsumer { + + private static final Logger logger = LoggerFactory.getLogger(MessageConsumer.class); + + @RabbitListener(queues = {SpringAmqpConfig.queueName}) + public void receiveMessage(String message) { + logger.info("Received Message: " + message); + } +} \ No newline at end of file diff --git a/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/MessageController.java b/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/MessageController.java new file mode 100644 index 000000000000..deef22c4d65f --- /dev/null +++ b/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/MessageController.java @@ -0,0 +1,26 @@ +package com.baeldung.springamqpsimple; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Controller; +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.ResponseStatus; + +@Controller +public class MessageController { + + private final MessageProducer messageProducer; + + @Autowired + public MessageController(MessageProducer messageProducer) { + this.messageProducer = messageProducer; + } + + @RequestMapping(value="/messages", method= RequestMethod.POST) + @ResponseStatus(value= HttpStatus.CREATED) + public void sendMessage(@RequestBody String message) { + messageProducer.sendMessage(message); + } +} diff --git a/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/MessageProducer.java b/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/MessageProducer.java new file mode 100644 index 000000000000..225f37bdd0f8 --- /dev/null +++ b/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/MessageProducer.java @@ -0,0 +1,20 @@ +package com.baeldung.springamqpsimple; + +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class MessageProducer { + + private final RabbitTemplate rabbitTemplate; + + @Autowired + public MessageProducer(RabbitTemplate rabbitTemplate) { + this.rabbitTemplate = rabbitTemplate; + } + + public void sendMessage(String message) { + rabbitTemplate.convertAndSend(SpringAmqpConfig.queueName, message); + } +} diff --git a/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/SpringAmqpApplication.java b/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/SpringAmqpApplication.java new file mode 100644 index 000000000000..b84a49a23028 --- /dev/null +++ b/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/SpringAmqpApplication.java @@ -0,0 +1,12 @@ +package com.baeldung.springamqpsimple; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringAmqpApplication { + + public static void main(String[] args) throws InterruptedException { + SpringApplication.run(SpringAmqpApplication.class, args); + } +} diff --git a/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/SpringAmqpConfig.java b/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/SpringAmqpConfig.java new file mode 100644 index 000000000000..f6c82b635ed1 --- /dev/null +++ b/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/SpringAmqpConfig.java @@ -0,0 +1,51 @@ +package com.baeldung.springamqpsimple; + +import org.springframework.amqp.core.Binding; +import org.springframework.amqp.core.BindingBuilder; +import org.springframework.amqp.core.DirectExchange; +import org.springframework.amqp.core.Exchange; +import org.springframework.amqp.core.Queue; +import org.springframework.amqp.rabbit.connection.ConnectionFactory; +import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; +import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; + +@Configuration +@Profile("!test") +public class SpringAmqpConfig { + + public final static String queueName = "com.baeldung.spring-amqp-simple.queue"; + public final static String exchangeName = "com.baeldung.spring-amqp-simple.exchange"; + + @Bean + Queue queue() { + return new Queue(queueName, false); + } + + @Bean + Exchange exchange() { + return new DirectExchange(exchangeName); + } + + @Bean + Binding binding(Queue queue, DirectExchange exchange) { + return BindingBuilder.bind(queue).to(exchange).with(queueName); + } + + @Bean + SimpleMessageListenerContainer container(ConnectionFactory connectionFactory, + MessageListenerAdapter listenerAdapter) { + SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(); + container.setConnectionFactory(connectionFactory); + container.setQueueNames(queueName); + container.setMessageListener(listenerAdapter); + return container; + } + + @Bean + MessageListenerAdapter listenerAdapter(MessageConsumer messageReceiver) { + return new MessageListenerAdapter(messageReceiver, "receiveMessage"); + } +} diff --git a/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/broadcast/BroadcastConfig.java b/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/broadcast/BroadcastConfig.java new file mode 100644 index 000000000000..1d02b4dad97b --- /dev/null +++ b/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/broadcast/BroadcastConfig.java @@ -0,0 +1,70 @@ +package com.baeldung.springamqpsimple.broadcast; + +import org.springframework.amqp.core.BindingBuilder; +import org.springframework.amqp.core.Declarable; +import org.springframework.amqp.core.DirectExchange; +import org.springframework.amqp.core.FanoutExchange; +import org.springframework.amqp.core.Queue; +import org.springframework.amqp.core.TopicExchange; +import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; +import org.springframework.amqp.rabbit.connection.ConnectionFactory; +import org.springframework.boot.autoconfigure.amqp.SimpleRabbitListenerContainerFactoryConfigurer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; + +import java.util.Arrays; +import java.util.List; + +@Configuration +@Profile("!test") +public class BroadcastConfig { + + public final static String fanoutQueue1Name = "com.baeldung.spring-amqp-simple.fanout.queue1"; + public final static String fanoutQueue2Name = "com.baeldung.spring-amqp-simple.fanout.queue2"; + public final static String fanoutExchangeName = "com.baeldung.spring-amqp-simple.fanout.exchange"; + + public final static String topicQueue1Name = "com.baeldung.spring-amqp-simple.topic.queue1"; + public final static String topicQueue2Name = "com.baeldung.spring-amqp-simple.topic.queue2"; + public final static String topicExchangeName = "com.baeldung.spring-amql-simple.topic.exchange"; + + @Bean + public List topicBindings() { + Queue topicQueue1 = new Queue(topicQueue1Name, false); + Queue topicQueue2 = new Queue(topicQueue2Name, false); + + TopicExchange topicExchange = new TopicExchange(topicExchangeName); + + return Arrays.asList( + topicQueue1, + topicQueue2, + topicExchange, + BindingBuilder.bind(topicQueue1).to(topicExchange).with("*.important.*"), + BindingBuilder.bind(topicQueue2).to(topicExchange).with("user.#") + ); + } + + @Bean + public List fanoutBindings() { + Queue fanoutQueue1 = new Queue(fanoutQueue1Name, false); + Queue fanoutQueue2 = new Queue(fanoutQueue2Name, false); + + FanoutExchange fanoutExchange = new FanoutExchange(fanoutExchangeName); + + return Arrays.asList( + fanoutQueue1, + fanoutQueue2, + fanoutExchange, + BindingBuilder.bind(fanoutQueue1).to(fanoutExchange), + BindingBuilder.bind(fanoutQueue2).to(fanoutExchange) + ); + } + + @Bean + public SimpleRabbitListenerContainerFactory container(ConnectionFactory connectionFactory, SimpleRabbitListenerContainerFactoryConfigurer configurer) { + SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory(); + configurer.configure(factory, connectionFactory); + return factory; + } + +} diff --git a/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/broadcast/BroadcastMessageConsumers.java b/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/broadcast/BroadcastMessageConsumers.java new file mode 100644 index 000000000000..0ae04ea09200 --- /dev/null +++ b/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/broadcast/BroadcastMessageConsumers.java @@ -0,0 +1,32 @@ +package com.baeldung.springamqpsimple.broadcast; + +import com.baeldung.springamqpsimple.MessageConsumer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.stereotype.Component; + +@Component +public class BroadcastMessageConsumers { + private static final Logger logger = LoggerFactory.getLogger(MessageConsumer.class); + + @RabbitListener(queues = {BroadcastConfig.fanoutQueue1Name}) + public void receiveMessageFromFanout1(String message) { + logger.info("Received fanout 1 message: " + message); + } + + @RabbitListener(queues = {BroadcastConfig.fanoutQueue2Name}) + public void receiveMessageFromFanout2(String message) { + logger.info("Received fanout 2 message: " + message); + } + + @RabbitListener(queues = {BroadcastConfig.topicQueue1Name}) + public void receiveMessageFromTopic1(String message) { + logger.info("Received topic 1 message: " + message); + } + + @RabbitListener(queues = {BroadcastConfig.topicQueue2Name}) + public void receiveMessageFromTopic2(String message) { + logger.info("Received topic 2 message: " + message); + } +} diff --git a/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/broadcast/BroadcastMessageController.java b/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/broadcast/BroadcastMessageController.java new file mode 100644 index 000000000000..75d4d20e552d --- /dev/null +++ b/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/broadcast/BroadcastMessageController.java @@ -0,0 +1,26 @@ +package com.baeldung.springamqpsimple.broadcast; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Controller; +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.ResponseStatus; + +@Controller +public class BroadcastMessageController { + + private final BroadcastMessageProducer messageProducer; + + @Autowired + public BroadcastMessageController(BroadcastMessageProducer messageProducer) { + this.messageProducer = messageProducer; + } + + @RequestMapping(value="/broadcast", method= RequestMethod.POST) + @ResponseStatus(value= HttpStatus.CREATED) + public void sendMessage(@RequestBody String message) { + messageProducer.sendMessages(message); + } +} diff --git a/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/broadcast/BroadcastMessageProducer.java b/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/broadcast/BroadcastMessageProducer.java new file mode 100644 index 000000000000..590da249620f --- /dev/null +++ b/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/broadcast/BroadcastMessageProducer.java @@ -0,0 +1,22 @@ +package com.baeldung.springamqpsimple.broadcast; + +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class BroadcastMessageProducer { + + private final RabbitTemplate rabbitTemplate; + + @Autowired + public BroadcastMessageProducer(RabbitTemplate rabbitTemplate) { + this.rabbitTemplate = rabbitTemplate; + } + + public void sendMessages(String message) { + rabbitTemplate.convertAndSend(BroadcastConfig.fanoutExchangeName, "", message); + rabbitTemplate.convertAndSend(BroadcastConfig.topicExchangeName, "user.not-important.info", message); + rabbitTemplate.convertAndSend(BroadcastConfig.topicExchangeName, "user.important.error", message); + } +} diff --git a/spring-amqp-simple/src/main/resources/application.yaml b/spring-amqp-simple/src/main/resources/application.yaml new file mode 100644 index 000000000000..47e7a3b9e087 --- /dev/null +++ b/spring-amqp-simple/src/main/resources/application.yaml @@ -0,0 +1,5 @@ +spring: + rabbitmq: + username: guest + password: guest + host: 10.10.10.105 \ No newline at end of file diff --git a/spring-amqp-simple/src/test/java/broadcast/BroadcastMessageControllerIntegrationTest.java b/spring-amqp-simple/src/test/java/broadcast/BroadcastMessageControllerIntegrationTest.java new file mode 100644 index 000000000000..c3be7f1edef4 --- /dev/null +++ b/spring-amqp-simple/src/test/java/broadcast/BroadcastMessageControllerIntegrationTest.java @@ -0,0 +1,48 @@ +package broadcast; + +import com.baeldung.springamqpsimple.broadcast.BroadcastConfig; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +@RunWith(SpringRunner.class) +@ActiveProfiles("test") +@SpringBootTest(webEnvironment = RANDOM_PORT) +public class BroadcastMessageControllerIntegrationTest { + + @Autowired + private TestRestTemplate restTemplate; + + @MockBean + private RabbitTemplate rabbitTemplate; + + @Test + public void whenPostingMessage_thenMessageIsCreated() { + final String message = "Hello World!"; + ResponseEntity responseEntity = restTemplate.postForEntity("/broadcast", message, Void.class); + + assertEquals(HttpStatus.CREATED, responseEntity.getStatusCode()); + } + + @Test + public void whenPostingMessage_thenMessageIsSentToBroker() { + final String message = "Hello World!"; + restTemplate.postForEntity("/broadcast", message, Void.class); + + verify(rabbitTemplate).convertAndSend(BroadcastConfig.fanoutExchangeName, "", message); + verify(rabbitTemplate).convertAndSend(BroadcastConfig.topicExchangeName, "user.not-important.info", message); + verify(rabbitTemplate).convertAndSend(BroadcastConfig.topicExchangeName, "user.important.error", message); + } +} \ No newline at end of file diff --git a/spring-amqp-simple/src/test/java/com/baeldung/springamqpsimple/MessageControllerIntegrationTest.java b/spring-amqp-simple/src/test/java/com/baeldung/springamqpsimple/MessageControllerIntegrationTest.java new file mode 100644 index 000000000000..a053edaa0e59 --- /dev/null +++ b/spring-amqp-simple/src/test/java/com/baeldung/springamqpsimple/MessageControllerIntegrationTest.java @@ -0,0 +1,46 @@ +package com.baeldung.springamqpsimple; + + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.verify; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +@RunWith(SpringRunner.class) +@ActiveProfiles("test") +@SpringBootTest(webEnvironment = RANDOM_PORT) +public class MessageControllerIntegrationTest { + + @Autowired + private TestRestTemplate restTemplate; + + @MockBean + private RabbitTemplate rabbitTemplate; + + @Test + public void whenPostingMessage_thenMessageIsCreated() { + final String message = "Hello World!"; + ResponseEntity responseEntity = restTemplate.postForEntity("/messages", message, Void.class); + + assertEquals(HttpStatus.CREATED, responseEntity.getStatusCode()); + } + + @Test + public void whenPostingMessage_thenMessageIsSentToBroker() { + final String message = "Hello World!"; + restTemplate.postForEntity("/messages", message, Void.class); + + verify(rabbitTemplate).convertAndSend(SpringAmqpConfig.queueName, message); + } +} \ No newline at end of file diff --git a/spring-amqp/pom.xml b/spring-amqp/pom.xml index bb26b2d15dda..4eef6070c7cf 100755 --- a/spring-amqp/pom.xml +++ b/spring-amqp/pom.xml @@ -20,6 +20,12 @@ org.springframework.amqp spring-rabbit 1.6.6.RELEASE + + + commons-logging + commons-logging + + diff --git a/spring-aop/README.md b/spring-aop/README.md new file mode 100644 index 000000000000..28295faf230e --- /dev/null +++ b/spring-aop/README.md @@ -0,0 +1,3 @@ +### Relevant articles + +- [Implementing a Custom Spring AOP Annotation](http://www.baeldung.com/spring-aop-annotation) diff --git a/spring-aop/pom.xml b/spring-aop/pom.xml new file mode 100644 index 000000000000..90e892b31101 --- /dev/null +++ b/spring-aop/pom.xml @@ -0,0 +1,59 @@ + + 4.0.0 + com.baeldung + spring-aop + 0.0.1-SNAPSHOT + war + spring-aop + + + org.springframework.boot + spring-boot-starter-parent + 1.5.2.RELEASE + + + + + + org.springframework.boot + spring-boot-starter-aop + + + + org.springframework.boot + spring-boot-starter-test + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*IntegrationTest.java + **/*LiveTest.java + + + + + + + diff --git a/spring-aop/src/main/java/org/baeldung/Application.java b/spring-aop/src/main/java/org/baeldung/Application.java new file mode 100644 index 000000000000..e5c764ef7ebe --- /dev/null +++ b/spring-aop/src/main/java/org/baeldung/Application.java @@ -0,0 +1,13 @@ +package org.baeldung; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/spring-aop/src/main/java/org/baeldung/ExampleAspect.java b/spring-aop/src/main/java/org/baeldung/ExampleAspect.java new file mode 100644 index 000000000000..7c3b5fb599ed --- /dev/null +++ b/spring-aop/src/main/java/org/baeldung/ExampleAspect.java @@ -0,0 +1,25 @@ +package org.baeldung; + +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.stereotype.Component; + +@Aspect +@Component +public class ExampleAspect { + + @Around("@annotation(LogExecutionTime)") + public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable { + final long start = System.currentTimeMillis(); + + final Object proceed = joinPoint.proceed(); + + final long executionTime = System.currentTimeMillis() - start; + + System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms"); + + return proceed; + } + +} diff --git a/spring-aop/src/main/java/org/baeldung/LogExecutionTime.java b/spring-aop/src/main/java/org/baeldung/LogExecutionTime.java new file mode 100644 index 000000000000..c10f97e78f52 --- /dev/null +++ b/spring-aop/src/main/java/org/baeldung/LogExecutionTime.java @@ -0,0 +1,11 @@ +package org.baeldung; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface LogExecutionTime { +} diff --git a/spring-aop/src/main/java/org/baeldung/Service.java b/spring-aop/src/main/java/org/baeldung/Service.java new file mode 100644 index 000000000000..e4bee3843844 --- /dev/null +++ b/spring-aop/src/main/java/org/baeldung/Service.java @@ -0,0 +1,12 @@ +package org.baeldung; + +import org.springframework.stereotype.Component; + +@Component +public class Service { + + @LogExecutionTime + public void serve() throws InterruptedException { + Thread.sleep(2000); + } +} diff --git a/spring-aop/src/test/java/org/baeldung/CustomAnnotationIntegrationTest.java b/spring-aop/src/test/java/org/baeldung/CustomAnnotationIntegrationTest.java new file mode 100644 index 000000000000..b109e3a39ee2 --- /dev/null +++ b/spring-aop/src/test/java/org/baeldung/CustomAnnotationIntegrationTest.java @@ -0,0 +1,21 @@ +package org.baeldung; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest +public class CustomAnnotationIntegrationTest { + + @Autowired + private Service service; + + @Test + public void shouldApplyCustomAnnotation() throws InterruptedException { + service.serve(); + } + +} diff --git a/spring-apache-camel/pom.xml b/spring-apache-camel/pom.xml index da7dad1a1fc8..0b1b10a7306a 100644 --- a/spring-apache-camel/pom.xml +++ b/spring-apache-camel/pom.xml @@ -35,6 +35,12 @@ org.apache.camel camel-spring ${env.camel.version} + + + commons-logging + commons-logging + + @@ -48,11 +54,6 @@ spring-context ${env.spring.version} - - org.slf4j - slf4j-log4j12 - 1.7.21 - org.apache.camel camel-spring-javaconfig diff --git a/spring-apache-camel/src/main/resources/log4j.xml b/spring-apache-camel/src/main/resources/log4j.xml deleted file mode 100644 index 4a86fb23573a..000000000000 --- a/spring-apache-camel/src/main/resources/log4j.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/spring-autowire/README.md b/spring-autowire/README.md deleted file mode 100644 index d5b8221b2599..000000000000 --- a/spring-autowire/README.md +++ /dev/null @@ -1,2 +0,0 @@ -### Relevant Articles: -- [Guide to Spring @Autowired](http://www.baeldung.com/spring-autowire) diff --git a/spring-autowire/pom.xml b/spring-autowire/pom.xml deleted file mode 100644 index 391cfc018ea3..000000000000 --- a/spring-autowire/pom.xml +++ /dev/null @@ -1,105 +0,0 @@ - - 4.0.0 - - com.baeldung - spring-autowire - 0.0.1-SNAPSHOT - jar - - spring-autowire - http://maven.apache.org - - - UTF-8 - 4.3.4.RELEASE - 4.12 - 3.6.0 - 2.19.1 - - - - - junit - junit - ${junit.version} - - - org.springframework - spring-core - ${org.springframework.version} - - - org.springframework - spring-context - ${org.springframework.version} - - - org.springframework - spring-test - ${org.springframework.version} - test - - - - - spring-autowire - - - org.apache.maven.plugins - maven-compiler-plugin - ${maven-compiler-plugin.version} - - 1.8 - 1.8 - - - - org.apache.maven.plugins - maven-surefire-plugin - ${maven-surefire-plugin.version} - - - **/*IntegrationTest.java - **/*LiveTest.java - - - - - - - - - integration - - - - org.apache.maven.plugins - maven-surefire-plugin - - - integration-test - - test - - - - **/*LiveTest.java - - - **/*IntegrationTest.java - - - - - - - json - - - - - - - - diff --git a/spring-autowire/src/main/java/com/baeldung/autowire/sample/Bar.java b/spring-autowire/src/main/java/com/baeldung/autowire/sample/Bar.java deleted file mode 100644 index 7aa820adefc7..000000000000 --- a/spring-autowire/src/main/java/com/baeldung/autowire/sample/Bar.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.baeldung.autowire.sample; - -public class Bar { - -} diff --git a/spring-autowire/src/main/java/com/baeldung/autowire/sample/Foo.java b/spring-autowire/src/main/java/com/baeldung/autowire/sample/Foo.java deleted file mode 100644 index b587ab38b81d..000000000000 --- a/spring-autowire/src/main/java/com/baeldung/autowire/sample/Foo.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.baeldung.autowire.sample; - -public class Foo { - -} diff --git a/spring-autowire/src/main/java/com/baeldung/autowire/sample/FooDAO.java b/spring-autowire/src/main/java/com/baeldung/autowire/sample/FooDAO.java deleted file mode 100644 index aec26202aba9..000000000000 --- a/spring-autowire/src/main/java/com/baeldung/autowire/sample/FooDAO.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.baeldung.autowire.sample; - -public class FooDAO { - -} diff --git a/spring-batch/pom.xml b/spring-batch/pom.xml index a92a0e83233c..8885bd452cb1 100644 --- a/spring-batch/pom.xml +++ b/spring-batch/pom.xml @@ -27,6 +27,12 @@ org.springframework spring-oxm ${spring.version} + + + commons-logging + commons-logging + + org.springframework diff --git a/spring-boot-custom-starter/greeter-spring-boot-autoconfigure/pom.xml b/spring-boot-custom-starter/greeter-spring-boot-autoconfigure/pom.xml new file mode 100644 index 000000000000..62c3676ae4d2 --- /dev/null +++ b/spring-boot-custom-starter/greeter-spring-boot-autoconfigure/pom.xml @@ -0,0 +1,59 @@ + + 4.0.0 + + com.baeldung + greeter-spring-boot-autoconfigure + 0.0.1-SNAPSHOT + + + UTF-8 + 1.5.2.RELEASE + 0.0.1-SNAPSHOT + + + + + + org.springframework.boot + spring-boot + ${spring-boot.version} + + + + org.springframework.boot + spring-boot-autoconfigure + ${spring-boot.version} + + + + org.springframework.boot + spring-boot-configuration-processor + ${spring-boot.version} + true + + + + com.baeldung + greeter + ${greeter.version} + true + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.5.1 + + 1.8 + 1.8 + + + + + + \ No newline at end of file diff --git a/spring-boot-custom-starter/greeter-spring-boot-autoconfigure/src/main/java/com/baeldung/greeter/autoconfigure/GreeterAutoConfiguration.java b/spring-boot-custom-starter/greeter-spring-boot-autoconfigure/src/main/java/com/baeldung/greeter/autoconfigure/GreeterAutoConfiguration.java new file mode 100644 index 000000000000..4cf078aa00c0 --- /dev/null +++ b/spring-boot-custom-starter/greeter-spring-boot-autoconfigure/src/main/java/com/baeldung/greeter/autoconfigure/GreeterAutoConfiguration.java @@ -0,0 +1,48 @@ +package com.baeldung.greeter.autoconfigure; + +import static com.baeldung.greeter.GreeterConfigParams.*; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.baeldung.greeter.Greeter; +import com.baeldung.greeter.GreetingConfig; + +@Configuration +@ConditionalOnClass(Greeter.class) +@EnableConfigurationProperties(GreeterProperties.class) +public class GreeterAutoConfiguration { + + @Autowired + private GreeterProperties greeterProperties; + + @Bean + @ConditionalOnMissingBean + public GreetingConfig greeterConfig() { + + String userName = greeterProperties.getUserName() == null ? System.getProperty("user.name") : greeterProperties.getUserName(); + String morningMessage = greeterProperties.getMorningMessage() == null ? "Good Morning" : greeterProperties.getMorningMessage(); + String afternoonMessage = greeterProperties.getAfternoonMessage() == null ? "Good Afternoon" : greeterProperties.getAfternoonMessage(); + String eveningMessage = greeterProperties.getEveningMessage() == null ? "Good Evening" : greeterProperties.getEveningMessage(); + String nightMessage = greeterProperties.getNightMessage() == null ? "Good Night" : greeterProperties.getNightMessage(); + + GreetingConfig greetingConfig = new GreetingConfig(); + greetingConfig.put(USER_NAME, userName); + greetingConfig.put(MORNING_MESSAGE, morningMessage); + greetingConfig.put(AFTERNOON_MESSAGE, afternoonMessage); + greetingConfig.put(EVENING_MESSAGE, eveningMessage); + greetingConfig.put(NIGHT_MESSAGE, nightMessage); + return greetingConfig; + } + + @Bean + @ConditionalOnMissingBean + public Greeter greeter(GreetingConfig greetingConfig) { + return new Greeter(greetingConfig); + } + +} diff --git a/spring-boot-custom-starter/greeter-spring-boot-autoconfigure/src/main/java/com/baeldung/greeter/autoconfigure/GreeterProperties.java b/spring-boot-custom-starter/greeter-spring-boot-autoconfigure/src/main/java/com/baeldung/greeter/autoconfigure/GreeterProperties.java new file mode 100644 index 000000000000..c60c45c8c6c0 --- /dev/null +++ b/spring-boot-custom-starter/greeter-spring-boot-autoconfigure/src/main/java/com/baeldung/greeter/autoconfigure/GreeterProperties.java @@ -0,0 +1,54 @@ +package com.baeldung.greeter.autoconfigure; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties(prefix = "baeldung.greeter") +public class GreeterProperties { + + private String userName; + private String morningMessage; + private String afternoonMessage; + private String eveningMessage; + private String nightMessage; + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public String getMorningMessage() { + return morningMessage; + } + + public void setMorningMessage(String morningMessage) { + this.morningMessage = morningMessage; + } + + public String getAfternoonMessage() { + return afternoonMessage; + } + + public void setAfternoonMessage(String afternoonMessage) { + this.afternoonMessage = afternoonMessage; + } + + public String getEveningMessage() { + return eveningMessage; + } + + public void setEveningMessage(String eveningMessage) { + this.eveningMessage = eveningMessage; + } + + public String getNightMessage() { + return nightMessage; + } + + public void setNightMessage(String nightMessage) { + this.nightMessage = nightMessage; + } + +} diff --git a/spring-boot-custom-starter/greeter-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-custom-starter/greeter-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000000..418201227b1d --- /dev/null +++ b/spring-boot-custom-starter/greeter-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +com.baeldung.greeter.autoconfigure.GreeterAutoConfiguration \ No newline at end of file diff --git a/spring-boot-custom-starter/greeter-spring-boot-sample-app/pom.xml b/spring-boot-custom-starter/greeter-spring-boot-sample-app/pom.xml new file mode 100644 index 000000000000..bb1d35cfaadf --- /dev/null +++ b/spring-boot-custom-starter/greeter-spring-boot-sample-app/pom.xml @@ -0,0 +1,39 @@ + + 4.0.0 + + com.baeldung + greeter-spring-boot-sample-app + 0.0.1-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 1.5.2.RELEASE + + + + + UTF-8 + 1.8 + 0.0.1-SNAPSHOT + + + + + + com.baeldung + greeter-spring-boot-starter + ${greeter-starter.version} + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + \ No newline at end of file diff --git a/spring-boot-custom-starter/greeter-spring-boot-sample-app/src/main/java/com/baeldung/greeter/sample/GreeterSampleApplication.java b/spring-boot-custom-starter/greeter-spring-boot-sample-app/src/main/java/com/baeldung/greeter/sample/GreeterSampleApplication.java new file mode 100644 index 000000000000..3c875246484f --- /dev/null +++ b/spring-boot-custom-starter/greeter-spring-boot-sample-app/src/main/java/com/baeldung/greeter/sample/GreeterSampleApplication.java @@ -0,0 +1,25 @@ +package com.baeldung.greeter.sample; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import com.baeldung.greeter.Greeter; + +@SpringBootApplication +public class GreeterSampleApplication implements CommandLineRunner { + + @Autowired + private Greeter greeter; + + public static void main(String[] args) { + SpringApplication.run(GreeterSampleApplication.class, args); + } + + @Override + public void run(String... args) throws Exception { + String message = greeter.greet(); + System.out.println(message); + } +} diff --git a/spring-boot-custom-starter/greeter-spring-boot-sample-app/src/main/resources/application.properties b/spring-boot-custom-starter/greeter-spring-boot-sample-app/src/main/resources/application.properties new file mode 100644 index 000000000000..6136e602f702 --- /dev/null +++ b/spring-boot-custom-starter/greeter-spring-boot-sample-app/src/main/resources/application.properties @@ -0,0 +1,2 @@ +baeldung.greeter.userName=Baeldung +baeldung.greeter.afternoonMessage=Woha\ Afternoon diff --git a/spring-boot-custom-starter/greeter-spring-boot-sample-app/src/test/java/com/baeldung/greeter/sample/GreeterSampleApplicationTest.java b/spring-boot-custom-starter/greeter-spring-boot-sample-app/src/test/java/com/baeldung/greeter/sample/GreeterSampleApplicationTest.java new file mode 100644 index 000000000000..bdb8fd0112a8 --- /dev/null +++ b/spring-boot-custom-starter/greeter-spring-boot-sample-app/src/test/java/com/baeldung/greeter/sample/GreeterSampleApplicationTest.java @@ -0,0 +1,50 @@ +package com.baeldung.greeter.sample; + +import static org.junit.Assert.assertEquals; + +import java.time.LocalDateTime; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.baeldung.greeter.Greeter; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest(classes = GreeterSampleApplication.class) +public class GreeterSampleApplicationTest { + + @Autowired + private Greeter greeter; + + @Test + public void givenMorningTime_ifMorningMessage_thenSuccess() { + String expected = "Hello Baeldung, Good Morning"; + String actual = greeter.greet(LocalDateTime.of(2017, 3, 1, 6, 0)); + assertEquals(expected, actual); + } + + @Test + public void givenAfternoonTime_ifAfternoonMessage_thenSuccess() { + String expected = "Hello Baeldung, Woha Afternoon"; + String actual = greeter.greet(LocalDateTime.of(2017, 3, 1, 13, 0)); + assertEquals(expected, actual); + } + + @Test + public void givenEveningTime_ifEveningMessage_thenSuccess() { + String expected = "Hello Baeldung, Good Evening"; + String actual = greeter.greet(LocalDateTime.of(2017, 3, 1, 19, 0)); + assertEquals(expected, actual); + } + + @Test + public void givenNightTime_ifNightMessage_thenSuccess() { + String expected = "Hello Baeldung, Good Night"; + String actual = greeter.greet(LocalDateTime.of(2017, 3, 1, 21, 0)); + assertEquals(expected, actual); + } + +} diff --git a/spring-boot-custom-starter/greeter-spring-boot-starter/pom.xml b/spring-boot-custom-starter/greeter-spring-boot-starter/pom.xml new file mode 100644 index 000000000000..7b38c53c09ed --- /dev/null +++ b/spring-boot-custom-starter/greeter-spring-boot-starter/pom.xml @@ -0,0 +1,51 @@ + + 4.0.0 + + com.baeldung + greeter-spring-boot-starter + 0.0.1-SNAPSHOT + + + UTF-8 + 0.0.1-SNAPSHOT + 1.5.2.RELEASE + + + + + + org.springframework.boot + spring-boot-starter + ${spring-boot.version} + + + + com.baeldung + greeter-spring-boot-autoconfigure + ${project.version} + + + + com.baeldung + greeter + ${greeter.version} + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.5.1 + + 1.8 + 1.8 + + + + + + \ No newline at end of file diff --git a/spring-boot-custom-starter/greeter/README.md b/spring-boot-custom-starter/greeter/README.md new file mode 100644 index 000000000000..3b26cbddcb8d --- /dev/null +++ b/spring-boot-custom-starter/greeter/README.md @@ -0,0 +1,19 @@ +# Greeter App + +This app takes in the user's name and messages for different times of day as configuration parameters and outptus the greeting messge. For example it will take the name **John** and the message for morning time as **Good Morning** and output the message **Hello John, Good Morning**. + +## Usage + +Create and populate the class `GreetingConfig`, instantiate a `Greeter` using the `GreetingConfig` and use it get greeting messages: + +```java +GreetingConfig greetingConfig = new GreetingConfig(); +greetingConfig.put(USER_NAME, "World"); +greetingConfig.put(MORNING_MESSAGE, "Good Morning"); +greetingConfig.put(AFTERNOON_MESSAGE, "Good Afternoon"); +greetingConfig.put(EVENING_MESSAGE, "Good Evening"); +greetingConfig.put(NIGHT_MESSAGE, "Good Night"); + +Greeter greeter = new Greeter(greetingConfig); +greeter.greet(); +``` \ No newline at end of file diff --git a/spring-boot-custom-starter/greeter/pom.xml b/spring-boot-custom-starter/greeter/pom.xml new file mode 100644 index 000000000000..b0e37a9ed99e --- /dev/null +++ b/spring-boot-custom-starter/greeter/pom.xml @@ -0,0 +1,38 @@ + + 4.0.0 + + com.baeldung + greeter + 0.0.1-SNAPSHOT + + + UTF-8 + + + + + + junit + junit + 4.12 + test + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.5.1 + + 1.8 + 1.8 + + + + + + \ No newline at end of file diff --git a/spring-boot-custom-starter/greeter/src/main/java/com/baeldung/greeter/Greeter.java b/spring-boot-custom-starter/greeter/src/main/java/com/baeldung/greeter/Greeter.java new file mode 100644 index 000000000000..970561d1fd3b --- /dev/null +++ b/spring-boot-custom-starter/greeter/src/main/java/com/baeldung/greeter/Greeter.java @@ -0,0 +1,35 @@ +package com.baeldung.greeter; + +import static com.baeldung.greeter.GreeterConfigParams.*; + +import java.time.LocalDateTime; + +public class Greeter { + + private GreetingConfig greetingConfig; + + public Greeter(GreetingConfig greetingConfig) { + this.greetingConfig = greetingConfig; + } + + public String greet(LocalDateTime localDateTime) { + + String name = greetingConfig.getProperty(USER_NAME); + int hourOfDay = localDateTime.getHour(); + + if (hourOfDay >= 5 && hourOfDay < 12) { + return String.format("Hello %s, %s", name, greetingConfig.get(MORNING_MESSAGE)); + } else if (hourOfDay >= 12 && hourOfDay < 17) { + return String.format("Hello %s, %s", name, greetingConfig.get(AFTERNOON_MESSAGE)); + } else if (hourOfDay >= 17 && hourOfDay < 20) { + return String.format("Hello %s, %s", name, greetingConfig.get(EVENING_MESSAGE)); + } else { + return String.format("Hello %s, %s", name, greetingConfig.get(NIGHT_MESSAGE)); + } + } + + public String greet() { + return greet(LocalDateTime.now()); + } + +} diff --git a/spring-boot-custom-starter/greeter/src/main/java/com/baeldung/greeter/GreeterConfigParams.java b/spring-boot-custom-starter/greeter/src/main/java/com/baeldung/greeter/GreeterConfigParams.java new file mode 100644 index 000000000000..fa30b0f312ce --- /dev/null +++ b/spring-boot-custom-starter/greeter/src/main/java/com/baeldung/greeter/GreeterConfigParams.java @@ -0,0 +1,11 @@ +package com.baeldung.greeter; + +public class GreeterConfigParams { + + public static final String USER_NAME = "user.name"; + public static final String MORNING_MESSAGE = "morning.message"; + public static final String AFTERNOON_MESSAGE = "afternoon.message"; + public static final String EVENING_MESSAGE = "evening.message"; + public static final String NIGHT_MESSAGE = "night.message"; + +} diff --git a/spring-boot-custom-starter/greeter/src/main/java/com/baeldung/greeter/GreetingConfig.java b/spring-boot-custom-starter/greeter/src/main/java/com/baeldung/greeter/GreetingConfig.java new file mode 100644 index 000000000000..fbbe11cf4ff6 --- /dev/null +++ b/spring-boot-custom-starter/greeter/src/main/java/com/baeldung/greeter/GreetingConfig.java @@ -0,0 +1,9 @@ +package com.baeldung.greeter; + +import java.util.Properties; + +public class GreetingConfig extends Properties{ + + private static final long serialVersionUID = 5662570853707247891L; + +} diff --git a/spring-boot-custom-starter/greeter/src/test/java/com/baeldung/greeter/GreeterTest.java b/spring-boot-custom-starter/greeter/src/test/java/com/baeldung/greeter/GreeterTest.java new file mode 100644 index 000000000000..834f1447bd2c --- /dev/null +++ b/spring-boot-custom-starter/greeter/src/test/java/com/baeldung/greeter/GreeterTest.java @@ -0,0 +1,56 @@ +package com.baeldung.greeter; + +import static com.baeldung.greeter.GreeterConfigParams.*; +import static org.junit.Assert.assertEquals; + +import java.time.LocalDateTime; + +import org.junit.BeforeClass; +import org.junit.Test; + +public class GreeterTest { + + private static GreetingConfig greetingConfig; + + @BeforeClass + public static void initalizeGreetingConfig() { + greetingConfig = new GreetingConfig(); + greetingConfig.put(USER_NAME, "World"); + greetingConfig.put(MORNING_MESSAGE, "Good Morning"); + greetingConfig.put(AFTERNOON_MESSAGE, "Good Afternoon"); + greetingConfig.put(EVENING_MESSAGE, "Good Evening"); + greetingConfig.put(NIGHT_MESSAGE, "Good Night"); + } + + @Test + public void givenMorningTime_ifMorningMessage_thenSuccess() { + String expected = "Hello World, Good Morning"; + Greeter greeter = new Greeter(greetingConfig); + String actual = greeter.greet(LocalDateTime.of(2017, 3, 1, 6, 0)); + assertEquals(expected, actual); + } + + @Test + public void givenAfternoonTime_ifAfternoonMessage_thenSuccess() { + String expected = "Hello World, Good Afternoon"; + Greeter greeter = new Greeter(greetingConfig); + String actual = greeter.greet(LocalDateTime.of(2017, 3, 1, 13, 0)); + assertEquals(expected, actual); + } + + @Test + public void givenEveningTime_ifEveningMessage_thenSuccess() { + String expected = "Hello World, Good Evening"; + Greeter greeter = new Greeter(greetingConfig); + String actual = greeter.greet(LocalDateTime.of(2017, 3, 1, 19, 0)); + assertEquals(expected, actual); + } + + @Test + public void givenNightTime_ifNightMessage_thenSuccess() { + String expected = "Hello World, Good Night"; + Greeter greeter = new Greeter(greetingConfig); + String actual = greeter.greet(LocalDateTime.of(2017, 3, 1, 21, 0)); + assertEquals(expected, actual); + } +} diff --git a/spring-boot-custom-starter/pom.xml b/spring-boot-custom-starter/pom.xml new file mode 100644 index 000000000000..e24ff071d41f --- /dev/null +++ b/spring-boot-custom-starter/pom.xml @@ -0,0 +1,34 @@ + + 4.0.0 + com.baeldung + spring-boot-custom-starter + 0.0.1-SNAPSHOT + pom + + + UTF-8 + + + + greeter + greeter-spring-boot-autoconfigure + greeter-spring-boot-starter + greeter-spring-boot-sample-app + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.5.1 + + 1.8 + 1.8 + + + + + + \ No newline at end of file diff --git a/spring-boot-servlet/README.md b/spring-boot-servlet/README.md deleted file mode 100644 index 262a11fc3622..000000000000 --- a/spring-boot-servlet/README.md +++ /dev/null @@ -1,2 +0,0 @@ -###Relevant Articles: -- [How to Register a Servlet in a Java Web Application](http://www.baeldung.com/how-to-register-a-servlet-in-a-java-web-application/) \ No newline at end of file diff --git a/spring-boot-servlet/pom.xml b/spring-boot-servlet/pom.xml deleted file mode 100644 index 3818e3468fca..000000000000 --- a/spring-boot-servlet/pom.xml +++ /dev/null @@ -1,55 +0,0 @@ - - 4.0.0 - com.baeldung - spring-boot-servlet - 0.0.1-SNAPSHOT - war - spring-boot-servlet - - - org.springframework.boot - spring-boot-dependencies - 1.5.1.RELEASE - - - - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-tomcat - provided - - - - org.apache.tomcat.embed - tomcat-embed-core - ${tomcat.version} - - - org.apache.tomcat.embed - tomcat-embed-jasper - ${tomcat.version} - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - - - UTF-8 - 1.8 - 8.5.11 - - - diff --git a/spring-boot-servlet/src/main/java/META-INF/MANIFEST.MF b/spring-boot-servlet/src/main/java/META-INF/MANIFEST.MF deleted file mode 100644 index 69ebae17519c..000000000000 --- a/spring-boot-servlet/src/main/java/META-INF/MANIFEST.MF +++ /dev/null @@ -1,2 +0,0 @@ -Manifest-Version: 1.0 -Main-Class: com.baeldung.ApplicationMain diff --git a/spring-boot-servlet/src/main/resources/application.properties b/spring-boot-servlet/src/main/resources/application.properties deleted file mode 100644 index 4e9e2b4cf1e1..000000000000 --- a/spring-boot-servlet/src/main/resources/application.properties +++ /dev/null @@ -1,10 +0,0 @@ -#Server Configuration -#server.port=8080 -#server.context-path=/javabootdata -#Resource Handling -#spring.resources.static-locations=classpath:/WEB-INF/resources -#spring.mvc.view.prefix=/WEB-INF/ -#spring.mvc.view.suffix=.jsp -#spring.resources.cache-period=3600 -servlet.name=dispatcherExample -servlet.mapping=/dispatcherExampleURL \ No newline at end of file diff --git a/spring-boot/README.MD b/spring-boot/README.MD index d0a02c69fc2e..dfd13277811a 100644 --- a/spring-boot/README.MD +++ b/spring-boot/README.MD @@ -1,7 +1,8 @@ ###The Course The "REST With Spring" Classes: http://bit.ly/restwithspring -###Relevant Articles: +### Relevant Articles: + - [Quick Guide to @RestClientTest in Spring Boot](http://www.baeldung.com/restclienttest-in-spring-boot) - [Intro to Spring Boot Starters](http://www.baeldung.com/spring-boot-starters) - [A Guide to Spring in Eclipse STS](http://www.baeldung.com/eclipse-sts-spring) @@ -10,3 +11,12 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring - [The @ServletComponentScan Annotation in Spring Boot](http://www.baeldung.com/spring-servletcomponentscan) - [A Custom Data Binder in Spring MVC](http://www.baeldung.com/spring-mvc-custom-data-binder) - [Intro to Building an Application with Spring Boot](http://www.baeldung.com/intro-to-spring-boot) +- [How to Register a Servlet in a Java Web Application](http://www.baeldung.com/register-servlet) +- [Guide to Spring WebUtils and ServletRequestUtils](http://www.baeldung.com/spring-webutils-servletrequestutils) +- [Using Custom Banners in Spring Boot](http://www.baeldung.com/spring-boot-custom-banners) +- [Guide to Internationalization in Spring Boot](http://www.baeldung.com/spring-boot-internationalization) +- [Create a Custom FailureAnalyzer with Spring Boot](http://www.baeldung.com/spring-boot-failure-analyzer) +- [Configuring Separate Spring DataSource for Tests](http://www.baeldung.com/spring-testing-separate-data-source) +- [Dynamic DTO Validation Config Retrieved from DB](http://www.baeldung.com/spring-dynamic-dto-validation) +- [Custom Information in Spring Boot Info Endpoint](http://www.baeldung.com/spring-boot-info-actuator-custom) +- [Using @JsonComponent in Spring Boot](http://www.baeldung.com/spring-boot-jsoncomponent) diff --git a/spring-boot/pom.xml b/spring-boot/pom.xml index e77ab10afff5..b00af7fc94ff 100644 --- a/spring-boot/pom.xml +++ b/spring-boot/pom.xml @@ -12,7 +12,7 @@ org.springframework.boot spring-boot-starter-parent - 1.5.1.RELEASE + 1.5.2.RELEASE @@ -41,6 +41,24 @@ spring-boot-starter-security + + org.springframework.boot + spring-boot-starter-tomcat + provided + + + + org.apache.tomcat.embed + tomcat-embed-core + ${tomcat.version} + + + + org.apache.tomcat.embed + tomcat-embed-jasper + ${tomcat.version} + + io.dropwizard.metrics metrics-core @@ -49,6 +67,7 @@ com.h2database h2 + ${h2.version} @@ -88,12 +107,24 @@ ${jquery.version} + + com.google.guava + guava + 18.0 + + org.apache.tomcat tomcat-servlet-api ${tomee-servlet-api.version} provided + + + mysql + mysql-connector-java + 6.0.6 + @@ -140,6 +171,7 @@ **/*IntegrationTest.java **/*LiveTest.java + **/AutoconfigurationTest.java @@ -165,6 +197,7 @@ **/*LiveTest.java + **/AutoconfigurationTest.java **/*IntegrationTest.java @@ -181,6 +214,39 @@ + + autoconfiguration + + + + org.apache.maven.plugins + maven-surefire-plugin + + + integration-test + + test + + + + **/*LiveTest.java + **/*IntegrationTest.java + + + **/AutoconfigurationTest.java + + + + + + + json + + + + + + @@ -195,6 +261,7 @@ 3.3.7-1 3.1.7 8.5.11 + 1.4.194 diff --git a/spring-boot/src/main/java/com/baeldung/annotation/servletcomponentscan/SpringBootAnnotatedApp.java b/spring-boot/src/main/java/com/baeldung/annotation/servletcomponentscan/SpringBootAnnotatedApp.java index b4d416dd9623..d54fa5a7c18b 100644 --- a/spring-boot/src/main/java/com/baeldung/annotation/servletcomponentscan/SpringBootAnnotatedApp.java +++ b/spring-boot/src/main/java/com/baeldung/annotation/servletcomponentscan/SpringBootAnnotatedApp.java @@ -4,6 +4,8 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.ServletComponentScan; +import com.baeldung.autoconfiguration.MySQLAutoconfiguration; + /** * using the following annotations are equivalent: *
  • @@ -14,7 +16,7 @@ * @ServletComponentScan(basePackageClasses = {AttrListener.class, HelloFilter.class, HelloServlet.class, EchoServlet.class}) *
*/ -@SpringBootApplication +@SpringBootApplication(exclude=MySQLAutoconfiguration.class) @ServletComponentScan("com.baeldung.annotation.servletcomponentscan.components") public class SpringBootAnnotatedApp { diff --git a/spring-boot/src/main/java/com/baeldung/annotation/servletcomponentscan/SpringBootPlainApp.java b/spring-boot/src/main/java/com/baeldung/annotation/servletcomponentscan/SpringBootPlainApp.java index 8a39078aac78..44030f440b5d 100644 --- a/spring-boot/src/main/java/com/baeldung/annotation/servletcomponentscan/SpringBootPlainApp.java +++ b/spring-boot/src/main/java/com/baeldung/annotation/servletcomponentscan/SpringBootPlainApp.java @@ -3,7 +3,9 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ComponentScan; -@SpringBootApplication +import com.baeldung.autoconfiguration.MySQLAutoconfiguration; + +@SpringBootApplication(exclude=MySQLAutoconfiguration.class) @ComponentScan(basePackages = "com.baeldung.annotation.servletcomponentscan.components") public class SpringBootPlainApp { diff --git a/spring-boot/src/main/java/com/baeldung/autoconfiguration/MySQLAutoconfiguration.java b/spring-boot/src/main/java/com/baeldung/autoconfiguration/MySQLAutoconfiguration.java new file mode 100644 index 000000000000..bbe2a86e3945 --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/autoconfiguration/MySQLAutoconfiguration.java @@ -0,0 +1,121 @@ +package com.baeldung.autoconfiguration; + +import java.util.Arrays; +import java.util.Properties; + +import javax.persistence.EntityManagerFactory; +import javax.sql.DataSource; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.AutoConfigureOrder; +import org.springframework.boot.autoconfigure.condition.ConditionMessage; +import org.springframework.boot.autoconfigure.condition.ConditionMessage.Style; +import org.springframework.boot.autoconfigure.condition.ConditionOutcome; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.condition.ConditionalOnResource; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.boot.autoconfigure.condition.SpringBootCondition; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.context.annotation.Conditional; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import org.springframework.core.Ordered; +import org.springframework.core.env.Environment; +import org.springframework.core.type.AnnotatedTypeMetadata; +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.util.ClassUtils; + +@Configuration +@ConditionalOnClass(DataSource.class) +@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) +@PropertySource("classpath:mysql.properties") +public class MySQLAutoconfiguration { + @Autowired + private Environment env; + + @Bean + @ConditionalOnProperty(name = "usemysql", havingValue = "local") + @ConditionalOnMissingBean + public DataSource dataSource() { + final DriverManagerDataSource dataSource = new DriverManagerDataSource(); + + dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); + dataSource.setUrl("jdbc:mysql://localhost:3306/myDb?createDatabaseIfNotExist=true"); + dataSource.setUsername("mysqluser"); + dataSource.setPassword("mysqlpass"); + + return dataSource; + } + + @Bean(name = "dataSource") + @ConditionalOnProperty(name = "usemysql", havingValue = "custom") + @ConditionalOnMissingBean + public DataSource dataSource2() { + final DriverManagerDataSource dataSource = new DriverManagerDataSource(); + + dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); + dataSource.setUrl(env.getProperty("mysql.url")); + dataSource.setUsername(env.getProperty("mysql.user") != null ? env.getProperty("mysql.user") : ""); + dataSource.setPassword(env.getProperty("mysql.pass") != null ? env.getProperty("mysql.pass") : ""); + + return dataSource; + } + + @Bean + @ConditionalOnBean(name = "dataSource") + @ConditionalOnMissingBean + public LocalContainerEntityManagerFactoryBean entityManagerFactory() { + final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); + em.setDataSource(dataSource()); + em.setPackagesToScan(new String[] { "com.baeldung.autoconfiguration.example" }); + em.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); + if (additionalProperties() != null) { + em.setJpaProperties(additionalProperties()); + } + return em; + } + + @Bean + @ConditionalOnMissingBean(type = "JpaTransactionManager") + JpaTransactionManager transactionManager(final EntityManagerFactory entityManagerFactory) { + final JpaTransactionManager transactionManager = new JpaTransactionManager(); + transactionManager.setEntityManagerFactory(entityManagerFactory); + return transactionManager; + } + + @ConditionalOnResource(resources = "classpath:mysql.properties") + @Conditional(HibernateCondition.class) + final Properties additionalProperties() { + final Properties hibernateProperties = new Properties(); + + hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("mysql-hibernate.hbm2ddl.auto")); + hibernateProperties.setProperty("hibernate.dialect", env.getProperty("mysql-hibernate.dialect")); + hibernateProperties.setProperty("hibernate.show_sql", env.getProperty("mysql-hibernate.show_sql") != null ? env.getProperty("mysql-hibernate.show_sql") : "false"); + + return hibernateProperties; + } + + static class HibernateCondition extends SpringBootCondition { + + private static String[] CLASS_NAMES = { "org.hibernate.ejb.HibernateEntityManager", "org.hibernate.jpa.HibernateEntityManager" }; + + @Override + public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { + ConditionMessage.Builder message = ConditionMessage.forCondition("Hibernate"); + for (String className : CLASS_NAMES) { + if (ClassUtils.isPresent(className, context.getClassLoader())) { + return ConditionOutcome.match(message.found("class").items(Style.NORMAL, className)); + } + } + return ConditionOutcome.noMatch(message.didNotFind("class", "classes").items(Style.NORMAL, Arrays.asList(CLASS_NAMES))); + } + + } +} diff --git a/spring-boot/src/main/java/com/baeldung/autoconfiguration/example/AutoconfigurationApplication.java b/spring-boot/src/main/java/com/baeldung/autoconfiguration/example/AutoconfigurationApplication.java new file mode 100644 index 000000000000..f419dbf4fd2f --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/autoconfiguration/example/AutoconfigurationApplication.java @@ -0,0 +1,15 @@ +package com.baeldung.autoconfiguration.example; + +import javax.annotation.security.RolesAllowed; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class AutoconfigurationApplication { + @RolesAllowed("*") + public static void main(String[] args) { + System.setProperty("security.basic.enabled", "false"); + SpringApplication.run(AutoconfigurationApplication.class, args); + } +} diff --git a/spring-boot/src/main/java/com/baeldung/autoconfiguration/example/MyUser.java b/spring-boot/src/main/java/com/baeldung/autoconfiguration/example/MyUser.java new file mode 100644 index 000000000000..31ce0fd9691a --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/autoconfiguration/example/MyUser.java @@ -0,0 +1,27 @@ +package com.baeldung.autoconfiguration.example; + +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class MyUser { + @Id + private String email; + + public MyUser() { + } + + public MyUser(String email) { + super(); + this.email = email; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + +} diff --git a/spring-boot/src/main/java/com/baeldung/autoconfiguration/example/MyUserRepository.java b/spring-boot/src/main/java/com/baeldung/autoconfiguration/example/MyUserRepository.java new file mode 100644 index 000000000000..fa411bc0b8ec --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/autoconfiguration/example/MyUserRepository.java @@ -0,0 +1,7 @@ +package com.baeldung.autoconfiguration.example; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface MyUserRepository extends JpaRepository{ + +} diff --git a/spring-boot/src/main/java/com/baeldung/dynamicvalidation/ContactInfo.java b/spring-boot/src/main/java/com/baeldung/dynamicvalidation/ContactInfo.java new file mode 100644 index 000000000000..41f873b42a2d --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/dynamicvalidation/ContactInfo.java @@ -0,0 +1,26 @@ +package com.baeldung.dynamicvalidation; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import javax.validation.Constraint; +import javax.validation.Payload; + +@Constraint(validatedBy = { ContactInfoValidator.class }) +@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER }) +@Retention(RetentionPolicy.RUNTIME) +public @interface ContactInfo { + String message() default "Invalid value"; + + Class[] groups() default {}; + + Class[] payload() default {}; + +} diff --git a/spring-boot/src/main/java/com/baeldung/dynamicvalidation/ContactInfoValidator.java b/spring-boot/src/main/java/com/baeldung/dynamicvalidation/ContactInfoValidator.java new file mode 100644 index 000000000000..c12c28d00aa2 --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/dynamicvalidation/ContactInfoValidator.java @@ -0,0 +1,46 @@ +package com.baeldung.dynamicvalidation; + +import com.baeldung.dynamicvalidation.dao.ContactInfoExpressionRepository; +import com.baeldung.dynamicvalidation.model.ContactInfoExpression; +import org.apache.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.thymeleaf.util.StringUtils; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import java.util.regex.Pattern; + +public class ContactInfoValidator implements ConstraintValidator { + + private static final Logger LOG = Logger.getLogger(ContactInfoValidator.class); + + @Autowired + private ContactInfoExpressionRepository expressionRepository; + + @Value("${contactInfoType}") + String expressionType; + + private String pattern; + + @Override + public void initialize(final ContactInfo contactInfo) { + if (StringUtils.isEmptyOrWhitespace(expressionType)) { + LOG.error("Contact info type missing!"); + } else { + pattern = expressionRepository.findOne(expressionType) + .map(ContactInfoExpression::getPattern) + .orElse(""); + } + } + + @Override + public boolean isValid(final String value, final ConstraintValidatorContext context) { + if (!StringUtils.isEmptyOrWhitespace(pattern)) { + return Pattern.matches(pattern, value); + } + LOG.error("Contact info pattern missing!"); + return false; + } + +} diff --git a/spring-boot/src/main/java/com/baeldung/dynamicvalidation/DynamicValidationApp.java b/spring-boot/src/main/java/com/baeldung/dynamicvalidation/DynamicValidationApp.java new file mode 100644 index 000000000000..361a7b1c03e5 --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/dynamicvalidation/DynamicValidationApp.java @@ -0,0 +1,15 @@ +package com.baeldung.dynamicvalidation; + +import javax.annotation.security.RolesAllowed; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class DynamicValidationApp { + @RolesAllowed("*") + public static void main(String[] args) { + System.setProperty("security.basic.enabled", "false"); + SpringApplication.run(DynamicValidationApp.class, args); + } +} diff --git a/spring-boot/src/main/java/com/baeldung/dynamicvalidation/config/CustomerController.java b/spring-boot/src/main/java/com/baeldung/dynamicvalidation/config/CustomerController.java new file mode 100644 index 000000000000..f08271f30704 --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/dynamicvalidation/config/CustomerController.java @@ -0,0 +1,29 @@ +package com.baeldung.dynamicvalidation.config; + +import javax.validation.Valid; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import com.baeldung.dynamicvalidation.model.Customer; + +@Controller +public class CustomerController { + + @GetMapping("/customer") + public String getCustomerPage(Model model) { + return "customer"; + } + + @PostMapping("/customer") + public String validateCustomer(@Valid final Customer customer, final BindingResult result, final Model model) { + if (result.hasErrors()) { + model.addAttribute("message", "The information is invalid!"); + } else { + model.addAttribute("message", "The information is valid!"); + } + return "customer"; + } +} diff --git a/spring-boot/src/main/java/com/baeldung/dynamicvalidation/config/PersistenceConfig.java b/spring-boot/src/main/java/com/baeldung/dynamicvalidation/config/PersistenceConfig.java new file mode 100644 index 000000000000..d89dfc5fcd62 --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/dynamicvalidation/config/PersistenceConfig.java @@ -0,0 +1,24 @@ +package com.baeldung.dynamicvalidation.config; + +import javax.sql.DataSource; + +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; + +@EnableJpaRepositories("com.baeldung.dynamicvalidation.dao") +@EntityScan("com.baeldung.dynamicvalidation.model") +@Configuration +public class PersistenceConfig { + + @Bean + public DataSource dataSource() { + EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); + EmbeddedDatabase db = builder.setType(EmbeddedDatabaseType.H2).addScript("schema-expressions.sql").addScript("data-expressions.sql").build(); + return db; + } +} diff --git a/spring-boot/src/main/java/com/baeldung/dynamicvalidation/dao/ContactInfoExpressionRepository.java b/spring-boot/src/main/java/com/baeldung/dynamicvalidation/dao/ContactInfoExpressionRepository.java new file mode 100644 index 000000000000..0d7797b9961b --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/dynamicvalidation/dao/ContactInfoExpressionRepository.java @@ -0,0 +1,11 @@ +package com.baeldung.dynamicvalidation.dao; + +import java.util.Optional; + +import org.springframework.data.repository.Repository; + +import com.baeldung.dynamicvalidation.model.ContactInfoExpression; + +public interface ContactInfoExpressionRepository extends Repository { + Optional findOne(String id); +} diff --git a/spring-boot/src/main/java/com/baeldung/dynamicvalidation/model/ContactInfoExpression.java b/spring-boot/src/main/java/com/baeldung/dynamicvalidation/model/ContactInfoExpression.java new file mode 100644 index 000000000000..9c202b07c8fa --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/dynamicvalidation/model/ContactInfoExpression.java @@ -0,0 +1,40 @@ +package com.baeldung.dynamicvalidation.model; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class ContactInfoExpression { + + @Id + @Column(name = "expression_type") + private String type; + private String pattern; + + public ContactInfoExpression() { + + } + + public ContactInfoExpression(final String type, final String pattern) { + this.type = type; + this.pattern = pattern; + } + + public String getType() { + return type; + } + + public void setType(final String type) { + this.type = type; + } + + public String getPattern() { + return pattern; + } + + public void setPattern(final String pattern) { + this.pattern = pattern; + } + +} diff --git a/spring-boot/src/main/java/com/baeldung/dynamicvalidation/model/Customer.java b/spring-boot/src/main/java/com/baeldung/dynamicvalidation/model/Customer.java new file mode 100644 index 000000000000..78d35807939e --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/dynamicvalidation/model/Customer.java @@ -0,0 +1,46 @@ +package com.baeldung.dynamicvalidation.model; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.validation.constraints.NotNull; + +import com.baeldung.dynamicvalidation.ContactInfo; + +@Entity +public class Customer { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @ContactInfo + @NotNull + private String contactInfo; + + public Customer() { + } + + public Customer(final long id, final String contactInfo) { + this.id = id; + this.contactInfo = contactInfo; + } + + public String getContactInfo() { + return contactInfo; + } + + public void setContactInfo(final String contactInfo) { + this.contactInfo = contactInfo; + } + + public long getId() { + return id; + } + + public void setId(final long id) { + this.id = id; + } + +} diff --git a/spring-boot/src/main/java/com/baeldung/failureanalyzer/FailureAnalyzerApplication.java b/spring-boot/src/main/java/com/baeldung/failureanalyzer/FailureAnalyzerApplication.java new file mode 100644 index 000000000000..9d379cbc090b --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/failureanalyzer/FailureAnalyzerApplication.java @@ -0,0 +1,17 @@ +package com.baeldung.failureanalyzer; + +import javax.annotation.security.RolesAllowed; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import com.baeldung.autoconfiguration.MySQLAutoconfiguration; + +@SpringBootApplication(exclude=MySQLAutoconfiguration.class) +public class FailureAnalyzerApplication { + @RolesAllowed("*") + public static void main(String[] args) { + System.setProperty("security.basic.enabled", "false"); + SpringApplication.run(FailureAnalyzerApplication.class, args); + } +} diff --git a/spring-boot/src/main/java/com/baeldung/failureanalyzer/MyBeanNotOfRequiredTypeFailureAnalyzer.java b/spring-boot/src/main/java/com/baeldung/failureanalyzer/MyBeanNotOfRequiredTypeFailureAnalyzer.java new file mode 100644 index 000000000000..2bbae8944a8d --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/failureanalyzer/MyBeanNotOfRequiredTypeFailureAnalyzer.java @@ -0,0 +1,28 @@ +package com.baeldung.failureanalyzer; + +import org.springframework.beans.factory.BeanNotOfRequiredTypeException; +import org.springframework.boot.diagnostics.AbstractFailureAnalyzer; +import org.springframework.boot.diagnostics.FailureAnalysis; + +public class MyBeanNotOfRequiredTypeFailureAnalyzer + extends AbstractFailureAnalyzer { + + @Override + protected FailureAnalysis analyze(Throwable rootFailure, BeanNotOfRequiredTypeException cause) { + return new FailureAnalysis(getDescription(cause), getAction(cause), cause); + } + + private String getDescription(BeanNotOfRequiredTypeException ex) { + return String.format("The bean %s could not be injected as %s because it is of type %s", + ex.getBeanName(), + ex.getRequiredType().getName(), + ex.getActualType().getName()); + } + + private String getAction(BeanNotOfRequiredTypeException ex) { + return String.format("Consider creating a bean with name %s of type %s", + ex.getBeanName(), + ex.getRequiredType().getName()); + } + +} diff --git a/spring-boot/src/main/java/com/baeldung/failureanalyzer/MyDAO.java b/spring-boot/src/main/java/com/baeldung/failureanalyzer/MyDAO.java new file mode 100644 index 000000000000..ddaeb28574f0 --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/failureanalyzer/MyDAO.java @@ -0,0 +1,5 @@ +package com.baeldung.failureanalyzer; + +public class MyDAO { + +} diff --git a/spring-boot/src/main/java/com/baeldung/failureanalyzer/MySecondDAO.java b/spring-boot/src/main/java/com/baeldung/failureanalyzer/MySecondDAO.java new file mode 100644 index 000000000000..12dd73a05b6d --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/failureanalyzer/MySecondDAO.java @@ -0,0 +1,8 @@ +package com.baeldung.failureanalyzer; + +import org.springframework.stereotype.Repository; + +@Repository("myDAO") +public class MySecondDAO { + +} diff --git a/spring-boot/src/main/java/com/baeldung/failureanalyzer/MyService.java b/spring-boot/src/main/java/com/baeldung/failureanalyzer/MyService.java new file mode 100644 index 000000000000..72334ca8faa9 --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/failureanalyzer/MyService.java @@ -0,0 +1,13 @@ +package com.baeldung.failureanalyzer; + +import javax.annotation.Resource; + +import org.springframework.stereotype.Service; + +@Service +public class MyService { + + @Resource(name = "myDAO") + private MyDAO myDAO; + +} diff --git a/spring-boot/src/main/java/com/baeldung/git/CommitIdApplication.java b/spring-boot/src/main/java/com/baeldung/git/CommitIdApplication.java index cd696eae70dd..c077692edb59 100644 --- a/spring-boot/src/main/java/com/baeldung/git/CommitIdApplication.java +++ b/spring-boot/src/main/java/com/baeldung/git/CommitIdApplication.java @@ -6,7 +6,9 @@ import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.core.io.ClassPathResource; -@SpringBootApplication(scanBasePackages = { "com.baeldung.git" }) +import com.baeldung.autoconfiguration.MySQLAutoconfiguration; + +@SpringBootApplication(scanBasePackages = { "com.baeldung.git" }, exclude=MySQLAutoconfiguration.class) public class CommitIdApplication { public static void main(String[] args) { SpringApplication.run(CommitIdApplication.class, args); diff --git a/spring-boot/src/main/java/com/baeldung/internationalization/InternationalizationApp.java b/spring-boot/src/main/java/com/baeldung/internationalization/InternationalizationApp.java new file mode 100644 index 000000000000..b743eb4b6184 --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/internationalization/InternationalizationApp.java @@ -0,0 +1,17 @@ +package com.baeldung.internationalization; + +import javax.annotation.security.RolesAllowed; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import com.baeldung.autoconfiguration.MySQLAutoconfiguration; + +@SpringBootApplication(exclude=MySQLAutoconfiguration.class) +public class InternationalizationApp { + @RolesAllowed("*") + public static void main(String[] args) { + System.setProperty("security.basic.enabled", "false"); + SpringApplication.run(InternationalizationApp.class, args); + } +} diff --git a/spring-boot/src/main/java/com/baeldung/internationalization/config/MvcConfig.java b/spring-boot/src/main/java/com/baeldung/internationalization/config/MvcConfig.java new file mode 100644 index 000000000000..59f7fd3ba53e --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/internationalization/config/MvcConfig.java @@ -0,0 +1,38 @@ +package com.baeldung.internationalization.config; + +import java.util.Locale; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.LocaleResolver; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; +import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; +import org.springframework.web.servlet.i18n.SessionLocaleResolver; + +@Configuration +@EnableWebMvc +@ComponentScan(basePackages = "com.baeldung.internationalization.config") +public class MvcConfig extends WebMvcConfigurerAdapter { + + @Bean + public LocaleResolver localeResolver() { + SessionLocaleResolver slr = new SessionLocaleResolver(); + slr.setDefaultLocale(Locale.US); + return slr; + } + + @Bean + public LocaleChangeInterceptor localeChangeInterceptor() { + LocaleChangeInterceptor lci = new LocaleChangeInterceptor(); + lci.setParamName("lang"); + return lci; + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(localeChangeInterceptor()); + } +} diff --git a/spring-boot/src/main/java/com/baeldung/internationalization/config/PageController.java b/spring-boot/src/main/java/com/baeldung/internationalization/config/PageController.java new file mode 100644 index 000000000000..96a534b85377 --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/internationalization/config/PageController.java @@ -0,0 +1,14 @@ +package com.baeldung.internationalization.config; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +public class PageController { + + @GetMapping("/international") + public String getInternationalPage() { + return "international"; + } + +} diff --git a/spring-boot/src/main/java/com/baeldung/intro/App.java b/spring-boot/src/main/java/com/baeldung/intro/App.java index 30e1c2b5ba6e..9553d814ac9f 100644 --- a/spring-boot/src/main/java/com/baeldung/intro/App.java +++ b/spring-boot/src/main/java/com/baeldung/intro/App.java @@ -3,7 +3,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -@SpringBootApplication +import com.baeldung.autoconfiguration.MySQLAutoconfiguration; + +@SpringBootApplication(exclude=MySQLAutoconfiguration.class) public class App { public static void main( String[] args ) diff --git a/spring-boot/src/main/java/com/baeldung/servlets/ApplicationMain.java b/spring-boot/src/main/java/com/baeldung/servlets/ApplicationMain.java new file mode 100644 index 000000000000..8965e2f0130e --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/servlets/ApplicationMain.java @@ -0,0 +1,21 @@ +package com.baeldung.servlets; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.support.SpringBootServletInitializer; + +import com.baeldung.autoconfiguration.MySQLAutoconfiguration; + +@SpringBootApplication(exclude=MySQLAutoconfiguration.class) +public class ApplicationMain extends SpringBootServletInitializer { + + public static void main(String[] args) { + SpringApplication.run(ApplicationMain.class, args); + } + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(ApplicationMain.class); + } +} diff --git a/spring-boot-servlet/src/main/java/com/baeldung/configuration/WebAppInitializer.java b/spring-boot/src/main/java/com/baeldung/servlets/configuration/WebAppInitializer.java similarity index 96% rename from spring-boot-servlet/src/main/java/com/baeldung/configuration/WebAppInitializer.java rename to spring-boot/src/main/java/com/baeldung/servlets/configuration/WebAppInitializer.java index b7e22500f4a7..eadd40355a92 100644 --- a/spring-boot-servlet/src/main/java/com/baeldung/configuration/WebAppInitializer.java +++ b/spring-boot/src/main/java/com/baeldung/servlets/configuration/WebAppInitializer.java @@ -1,4 +1,4 @@ -package com.baeldung.configuration; +package com.baeldung.servlets.configuration; import org.springframework.web.WebApplicationInitializer; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; @@ -29,4 +29,4 @@ public void onStartup(ServletContext container) throws ServletException { servletTwo.addMapping("/"); } -} \ No newline at end of file +} diff --git a/spring-boot-servlet/src/main/java/com/baeldung/configuration/WebMvcConfigure.java b/spring-boot/src/main/java/com/baeldung/servlets/configuration/WebMvcConfigure.java similarity index 97% rename from spring-boot-servlet/src/main/java/com/baeldung/configuration/WebMvcConfigure.java rename to spring-boot/src/main/java/com/baeldung/servlets/configuration/WebMvcConfigure.java index de9067de6e58..3d6a10c2aca6 100644 --- a/spring-boot-servlet/src/main/java/com/baeldung/configuration/WebMvcConfigure.java +++ b/spring-boot/src/main/java/com/baeldung/servlets/configuration/WebMvcConfigure.java @@ -1,4 +1,4 @@ -package com.baeldung.configuration; +package com.baeldung.servlets.configuration; import org.springframework.boot.web.support.ErrorPageFilter; import org.springframework.context.annotation.Bean; @@ -37,4 +37,3 @@ public ErrorPageFilter errorPageFilter() { return new ErrorPageFilter(); } } - diff --git a/spring-boot-servlet/src/main/java/com/baeldung/props/Constants.java b/spring-boot/src/main/java/com/baeldung/servlets/props/Constants.java similarity index 95% rename from spring-boot-servlet/src/main/java/com/baeldung/props/Constants.java rename to spring-boot/src/main/java/com/baeldung/servlets/props/Constants.java index 421401eec733..6345d1f96956 100644 --- a/spring-boot-servlet/src/main/java/com/baeldung/props/Constants.java +++ b/spring-boot/src/main/java/com/baeldung/servlets/props/Constants.java @@ -1,4 +1,4 @@ -package com.baeldung.props; +package com.baeldung.servlets.props; import org.springframework.beans.factory.annotation.Autowired; diff --git a/spring-boot-servlet/src/main/java/com/baeldung/props/PropertyLoader.java b/spring-boot/src/main/java/com/baeldung/servlets/props/PropertyLoader.java similarity index 94% rename from spring-boot-servlet/src/main/java/com/baeldung/props/PropertyLoader.java rename to spring-boot/src/main/java/com/baeldung/servlets/props/PropertyLoader.java index 5d890d96fa11..c29da4592951 100644 --- a/spring-boot-servlet/src/main/java/com/baeldung/props/PropertyLoader.java +++ b/spring-boot/src/main/java/com/baeldung/servlets/props/PropertyLoader.java @@ -1,4 +1,4 @@ -package com.baeldung.props; +package com.baeldung.servlets.props; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/spring-boot-servlet/src/main/java/com/baeldung/props/PropertySourcesLoader.java b/spring-boot/src/main/java/com/baeldung/servlets/props/PropertySourcesLoader.java similarity index 95% rename from spring-boot-servlet/src/main/java/com/baeldung/props/PropertySourcesLoader.java rename to spring-boot/src/main/java/com/baeldung/servlets/props/PropertySourcesLoader.java index 8c7b3a4af56e..56a6751326c7 100644 --- a/spring-boot-servlet/src/main/java/com/baeldung/props/PropertySourcesLoader.java +++ b/spring-boot/src/main/java/com/baeldung/servlets/props/PropertySourcesLoader.java @@ -1,4 +1,4 @@ -package com.baeldung.props; +package com.baeldung.servlets.props; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/spring-boot-servlet/src/main/java/com/baeldung/servlets/GenericCustomServlet.java b/spring-boot/src/main/java/com/baeldung/servlets/servlets/GenericCustomServlet.java similarity index 93% rename from spring-boot-servlet/src/main/java/com/baeldung/servlets/GenericCustomServlet.java rename to spring-boot/src/main/java/com/baeldung/servlets/servlets/GenericCustomServlet.java index c6543c9eef6a..49dd9404b7d1 100644 --- a/spring-boot-servlet/src/main/java/com/baeldung/servlets/GenericCustomServlet.java +++ b/spring-boot/src/main/java/com/baeldung/servlets/servlets/GenericCustomServlet.java @@ -1,4 +1,4 @@ -package com.baeldung.servlets; +package com.baeldung.servlets.servlets; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; diff --git a/spring-boot-servlet/src/main/java/com/baeldung/servlets/javaee/AnnotationServlet.java b/spring-boot/src/main/java/com/baeldung/servlets/servlets/javaee/AnnotationServlet.java similarity index 93% rename from spring-boot-servlet/src/main/java/com/baeldung/servlets/javaee/AnnotationServlet.java rename to spring-boot/src/main/java/com/baeldung/servlets/servlets/javaee/AnnotationServlet.java index d971e68cfa6f..b50a7d545405 100644 --- a/spring-boot-servlet/src/main/java/com/baeldung/servlets/javaee/AnnotationServlet.java +++ b/spring-boot/src/main/java/com/baeldung/servlets/servlets/javaee/AnnotationServlet.java @@ -1,4 +1,4 @@ -package com.baeldung.servlets.javaee; +package com.baeldung.servlets.servlets.javaee; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; diff --git a/spring-boot-servlet/src/main/java/com/baeldung/servlets/javaee/EEWebXmlServlet.java b/spring-boot/src/main/java/com/baeldung/servlets/servlets/javaee/EEWebXmlServlet.java similarity index 92% rename from spring-boot-servlet/src/main/java/com/baeldung/servlets/javaee/EEWebXmlServlet.java rename to spring-boot/src/main/java/com/baeldung/servlets/servlets/javaee/EEWebXmlServlet.java index 4209e815cd73..c7b373064fdf 100644 --- a/spring-boot-servlet/src/main/java/com/baeldung/servlets/javaee/EEWebXmlServlet.java +++ b/spring-boot/src/main/java/com/baeldung/servlets/servlets/javaee/EEWebXmlServlet.java @@ -1,4 +1,4 @@ -package com.baeldung.servlets.javaee; +package com.baeldung.servlets.servlets.javaee; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -17,4 +17,4 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t PrintWriter out = response.getWriter(); out.println("

Hello World

"); } -} \ No newline at end of file +} diff --git a/spring-boot-servlet/src/main/java/com/baeldung/servlets/springboot/SpringRegistrationBeanServlet.java b/spring-boot/src/main/java/com/baeldung/servlets/servlets/springboot/SpringRegistrationBeanServlet.java similarity index 82% rename from spring-boot-servlet/src/main/java/com/baeldung/servlets/springboot/SpringRegistrationBeanServlet.java rename to spring-boot/src/main/java/com/baeldung/servlets/servlets/springboot/SpringRegistrationBeanServlet.java index 4a34465894ff..e3c225d429ff 100644 --- a/spring-boot-servlet/src/main/java/com/baeldung/servlets/springboot/SpringRegistrationBeanServlet.java +++ b/spring-boot/src/main/java/com/baeldung/servlets/servlets/springboot/SpringRegistrationBeanServlet.java @@ -1,6 +1,6 @@ -package com.baeldung.servlets.springboot; +package com.baeldung.servlets.servlets.springboot; -import com.baeldung.servlets.GenericCustomServlet; +import com.baeldung.servlets.servlets.GenericCustomServlet; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -15,5 +15,3 @@ public ServletRegistrationBean genericCustomServlet() { return bean; } } - - diff --git a/spring-boot-servlet/src/main/java/com/baeldung/servlets/springboot/embedded/EmbeddedTomcatExample.java b/spring-boot/src/main/java/com/baeldung/servlets/servlets/springboot/embedded/EmbeddedTomcatExample.java similarity index 90% rename from spring-boot-servlet/src/main/java/com/baeldung/servlets/springboot/embedded/EmbeddedTomcatExample.java rename to spring-boot/src/main/java/com/baeldung/servlets/servlets/springboot/embedded/EmbeddedTomcatExample.java index b2458f33c7c6..9e460d03a893 100644 --- a/spring-boot-servlet/src/main/java/com/baeldung/servlets/springboot/embedded/EmbeddedTomcatExample.java +++ b/spring-boot/src/main/java/com/baeldung/servlets/servlets/springboot/embedded/EmbeddedTomcatExample.java @@ -1,4 +1,4 @@ -package com.baeldung.servlets.springboot.embedded; +package com.baeldung.servlets.servlets.springboot.embedded; import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; diff --git a/spring-boot/src/main/java/com/baeldung/utils/Application.java b/spring-boot/src/main/java/com/baeldung/utils/Application.java index a3d9f9130c32..9d5d75bce2ab 100644 --- a/spring-boot/src/main/java/com/baeldung/utils/Application.java +++ b/spring-boot/src/main/java/com/baeldung/utils/Application.java @@ -6,7 +6,9 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ComponentScan; -@SpringBootApplication +import com.baeldung.autoconfiguration.MySQLAutoconfiguration; + +@SpringBootApplication(exclude=MySQLAutoconfiguration.class) @ComponentScan(basePackages="com.baeldung.utils") public class Application { diff --git a/spring-boot/src/main/java/com/baeldung/webjar/WebjarsdemoApplication.java b/spring-boot/src/main/java/com/baeldung/webjar/WebjarsdemoApplication.java index d2135754c950..44d48f5f8f5b 100644 --- a/spring-boot/src/main/java/com/baeldung/webjar/WebjarsdemoApplication.java +++ b/spring-boot/src/main/java/com/baeldung/webjar/WebjarsdemoApplication.java @@ -4,7 +4,9 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ComponentScan; -@SpringBootApplication +import com.baeldung.autoconfiguration.MySQLAutoconfiguration; + +@SpringBootApplication(exclude=MySQLAutoconfiguration.class) public class WebjarsdemoApplication { public static void main(String[] args) { diff --git a/spring-boot/src/main/java/org/baeldung/Application.java b/spring-boot/src/main/java/org/baeldung/Application.java index aae0c427a98a..c47bb90830c6 100644 --- a/spring-boot/src/main/java/org/baeldung/Application.java +++ b/spring-boot/src/main/java/org/baeldung/Application.java @@ -3,7 +3,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.context.ApplicationContext; -@org.springframework.boot.autoconfigure.SpringBootApplication +import com.baeldung.autoconfiguration.MySQLAutoconfiguration; + +@org.springframework.boot.autoconfigure.SpringBootApplication(exclude=MySQLAutoconfiguration.class) public class Application { private static ApplicationContext applicationContext; diff --git a/spring-boot/src/main/java/org/baeldung/boot/DemoApplication.java b/spring-boot/src/main/java/org/baeldung/boot/DemoApplication.java index e61d140396ff..2d83b650ecc4 100644 --- a/spring-boot/src/main/java/org/baeldung/boot/DemoApplication.java +++ b/spring-boot/src/main/java/org/baeldung/boot/DemoApplication.java @@ -3,7 +3,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -@SpringBootApplication +import com.baeldung.autoconfiguration.MySQLAutoconfiguration; + +@SpringBootApplication(exclude=MySQLAutoconfiguration.class) public class DemoApplication { public static void main(String[] args) { diff --git a/spring-boot/src/main/java/org/baeldung/boot/boottest/Employee.java b/spring-boot/src/main/java/org/baeldung/boot/boottest/Employee.java new file mode 100644 index 000000000000..a805e8f5fe25 --- /dev/null +++ b/spring-boot/src/main/java/org/baeldung/boot/boottest/Employee.java @@ -0,0 +1,43 @@ +package org.baeldung.boot.boottest; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.validation.constraints.Size; + +@Entity +@Table(name = "person") +public class Employee { + + public Employee() { + } + + public Employee(String name) { + this.name = name; + } + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + @Size(min = 3, max = 20) + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} \ No newline at end of file diff --git a/spring-boot/src/main/java/org/baeldung/boot/boottest/EmployeeRepository.java b/spring-boot/src/main/java/org/baeldung/boot/boottest/EmployeeRepository.java new file mode 100644 index 000000000000..fa234f0e3ae4 --- /dev/null +++ b/spring-boot/src/main/java/org/baeldung/boot/boottest/EmployeeRepository.java @@ -0,0 +1,21 @@ +package org.baeldung.boot.boottest; + +import java.util.List; +import java.util.Optional; + +import javax.transaction.Transactional; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +@Transactional +public interface EmployeeRepository extends JpaRepository { + + public Optional findByName(String name); + + public Optional findById(Long id); + + public List findAll(); + +} diff --git a/spring-boot/src/main/java/org/baeldung/boot/boottest/EmployeeRestController.java b/spring-boot/src/main/java/org/baeldung/boot/boottest/EmployeeRestController.java new file mode 100644 index 000000000000..1bfde0f0bd0e --- /dev/null +++ b/spring-boot/src/main/java/org/baeldung/boot/boottest/EmployeeRestController.java @@ -0,0 +1,33 @@ +package org.baeldung.boot.boottest; + +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api") +public class EmployeeRestController { + + @Autowired + private EmployeeService employeeService; + + @PostMapping("/employees") + public ResponseEntity createEmployee(@RequestBody Employee employee) { + HttpStatus status = HttpStatus.CREATED; + Employee saved = employeeService.save(employee); + return new ResponseEntity<>(saved, status); + } + + @GetMapping("/employees") + public List getAllEmployees() { + return employeeService.getAllEmployees(); + } + +} diff --git a/spring-boot/src/main/java/org/baeldung/boot/boottest/EmployeeService.java b/spring-boot/src/main/java/org/baeldung/boot/boottest/EmployeeService.java new file mode 100644 index 000000000000..f0ed49e69955 --- /dev/null +++ b/spring-boot/src/main/java/org/baeldung/boot/boottest/EmployeeService.java @@ -0,0 +1,17 @@ +package org.baeldung.boot.boottest; + +import java.util.List; +import java.util.Optional; + +public interface EmployeeService { + + public Optional getEmployeeById(Long id); + + public Optional getEmployeeByName(String name); + + public List getAllEmployees(); + + public boolean exists(String email); + + public Employee save(Employee employee); +} diff --git a/spring-boot/src/main/java/org/baeldung/boot/boottest/EmployeeServiceImpl.java b/spring-boot/src/main/java/org/baeldung/boot/boottest/EmployeeServiceImpl.java new file mode 100644 index 000000000000..6d5bf426ec7c --- /dev/null +++ b/spring-boot/src/main/java/org/baeldung/boot/boottest/EmployeeServiceImpl.java @@ -0,0 +1,45 @@ +package org.baeldung.boot.boottest; + +import java.util.List; +import java.util.Optional; + +import javax.transaction.Transactional; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +@Transactional +public class EmployeeServiceImpl implements EmployeeService { + + @Autowired + private EmployeeRepository employeeRepository; + + @Override + public Optional getEmployeeById(Long id) { + return employeeRepository.findById(id); + } + + @Override + public Optional getEmployeeByName(String name) { + return employeeRepository.findByName(name); + } + + @Override + public boolean exists(String name) { + if (employeeRepository.findByName(name) != null) { + return true; + } + return false; + } + + @Override + public Employee save(Employee employee) { + return employeeRepository.save(employee); + } + + @Override + public List getAllEmployees() { + return employeeRepository.findAll(); + } +} diff --git a/spring-boot/src/main/java/org/baeldung/boot/components/FooService.java b/spring-boot/src/main/java/org/baeldung/boot/components/FooService.java new file mode 100644 index 000000000000..235fd4329933 --- /dev/null +++ b/spring-boot/src/main/java/org/baeldung/boot/components/FooService.java @@ -0,0 +1,21 @@ +package org.baeldung.boot.components; + +import org.baeldung.boot.model.Foo; +import org.baeldung.boot.repository.FooRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class FooService { + + @Autowired + private FooRepository fooRepository; + + public Foo getFooWithId(Integer id) throws Exception { + return fooRepository.findOne(id); + } + + public Foo getFooWithName(String name) { + return fooRepository.findByName(name); + } +} \ No newline at end of file diff --git a/spring-boot/src/main/java/org/baeldung/boot/exceptions/CommonException.java b/spring-boot/src/main/java/org/baeldung/boot/exceptions/CommonException.java new file mode 100644 index 000000000000..1f008440e613 --- /dev/null +++ b/spring-boot/src/main/java/org/baeldung/boot/exceptions/CommonException.java @@ -0,0 +1,13 @@ +package org.baeldung.boot.exceptions; + +public class CommonException extends RuntimeException{ + + /** + * + */ + private static final long serialVersionUID = 3080004140659213332L; + + public CommonException(String message){ + super(message); + } +} diff --git a/spring-boot/src/main/java/org/baeldung/boot/exceptions/FooNotFoundException.java b/spring-boot/src/main/java/org/baeldung/boot/exceptions/FooNotFoundException.java new file mode 100644 index 000000000000..68ef3fa3894a --- /dev/null +++ b/spring-boot/src/main/java/org/baeldung/boot/exceptions/FooNotFoundException.java @@ -0,0 +1,13 @@ +package org.baeldung.boot.exceptions; + +public class FooNotFoundException extends RuntimeException{ + + /** + * + */ + private static final long serialVersionUID = 9042200028456133589L; + + public FooNotFoundException(String message){ + super(message); + } +} diff --git a/spring-boot/src/main/java/org/baeldung/boot/model/Foo.java b/spring-boot/src/main/java/org/baeldung/boot/model/Foo.java index 6a36459e3c5e..ac8a8fe42959 100644 --- a/spring-boot/src/main/java/org/baeldung/boot/model/Foo.java +++ b/spring-boot/src/main/java/org/baeldung/boot/model/Foo.java @@ -21,6 +21,13 @@ public Foo(String name) { this.name = name; } + + public Foo(Integer id, String name) { + super(); + this.id = id; + this.name = name; + } + public Integer getId() { return id; } diff --git a/spring-boot/src/main/java/org/baeldung/boot/model/User.java b/spring-boot/src/main/java/org/baeldung/boot/model/User.java new file mode 100644 index 000000000000..f60ac86fe496 --- /dev/null +++ b/spring-boot/src/main/java/org/baeldung/boot/model/User.java @@ -0,0 +1,41 @@ +package org.baeldung.boot.model; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name = "users") +public class User { + + @Id + @GeneratedValue + private Integer id; + private String name; + private Integer status; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } +} diff --git a/spring-boot/src/main/java/org/baeldung/boot/repository/UserRepository.java b/spring-boot/src/main/java/org/baeldung/boot/repository/UserRepository.java new file mode 100644 index 000000000000..3a419a65bdb4 --- /dev/null +++ b/spring-boot/src/main/java/org/baeldung/boot/repository/UserRepository.java @@ -0,0 +1,10 @@ +package org.baeldung.boot.repository; + +import org.baeldung.boot.model.User; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository("userRepository") +public interface UserRepository extends JpaRepository { + public int countByStatus(int status); +} diff --git a/spring-boot/src/main/java/org/baeldung/boot/service/FooController.java b/spring-boot/src/main/java/org/baeldung/boot/service/FooController.java new file mode 100644 index 000000000000..834fa342e25b --- /dev/null +++ b/spring-boot/src/main/java/org/baeldung/boot/service/FooController.java @@ -0,0 +1,26 @@ +package org.baeldung.boot.service; + +import org.baeldung.boot.components.FooService; +import org.baeldung.boot.model.Foo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class FooController { + + @Autowired + private FooService fooService; + + @GetMapping("/{id}") + public Foo getFooWithId(@PathVariable Integer id) throws Exception { + return fooService.getFooWithId(id); + } + + @GetMapping("/") + public Foo getFooWithName(@RequestParam String name) throws Exception { + return fooService.getFooWithName(name); + } +} \ No newline at end of file diff --git a/spring-boot/src/main/java/org/baeldung/config/H2JpaConfig.java b/spring-boot/src/main/java/org/baeldung/config/H2JpaConfig.java new file mode 100644 index 000000000000..ace7bb5a6d81 --- /dev/null +++ b/spring-boot/src/main/java/org/baeldung/config/H2JpaConfig.java @@ -0,0 +1,67 @@ +package org.baeldung.config; + +import java.util.Properties; + +import javax.persistence.EntityManagerFactory; +import javax.sql.DataSource; + +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.repository", "org.baeldung.boot.repository" }) +@PropertySource("classpath:persistence-generic-entity.properties") +@EnableTransactionManagement +public class H2JpaConfig { + + @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.domain", "org.baeldung.boot.model" }); + em.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); + em.setJpaProperties(additionalProperties()); + return em; + } + + @Bean + JpaTransactionManager transactionManager(final EntityManagerFactory entityManagerFactory) { + final 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")); + + return hibernateProperties; + } + +} diff --git a/spring-boot/src/main/java/org/baeldung/endpoints/info/TotalUsersInfoContributor.java b/spring-boot/src/main/java/org/baeldung/endpoints/info/TotalUsersInfoContributor.java new file mode 100644 index 000000000000..790584644fe5 --- /dev/null +++ b/spring-boot/src/main/java/org/baeldung/endpoints/info/TotalUsersInfoContributor.java @@ -0,0 +1,26 @@ +package org.baeldung.endpoints.info; + +import java.util.HashMap; +import java.util.Map; + +import org.baeldung.boot.repository.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.info.Info; +import org.springframework.boot.actuate.info.InfoContributor; +import org.springframework.stereotype.Component; + +@Component +public class TotalUsersInfoContributor implements InfoContributor { + + @Autowired + UserRepository userRepository; + + @Override + public void contribute(Info.Builder builder) { + Map userDetails = new HashMap<>(); + userDetails.put("active", userRepository.countByStatus(1)); + userDetails.put("inactive", userRepository.countByStatus(0)); + + builder.withDetail("users", userDetails); + } +} diff --git a/spring-boot/src/main/java/org/baeldung/jsoncomponent/User.java b/spring-boot/src/main/java/org/baeldung/jsoncomponent/User.java new file mode 100644 index 000000000000..89618745260e --- /dev/null +++ b/spring-boot/src/main/java/org/baeldung/jsoncomponent/User.java @@ -0,0 +1,15 @@ +package org.baeldung.jsoncomponent; + +import javafx.scene.paint.Color; + +public class User { + private final Color favoriteColor; + + public User(Color favoriteColor) { + this.favoriteColor = favoriteColor; + } + + public Color getFavoriteColor() { + return favoriteColor; + } +} diff --git a/spring-boot/src/main/java/org/baeldung/jsoncomponent/UserCombinedSerializer.java b/spring-boot/src/main/java/org/baeldung/jsoncomponent/UserCombinedSerializer.java new file mode 100644 index 000000000000..2001340197d2 --- /dev/null +++ b/spring-boot/src/main/java/org/baeldung/jsoncomponent/UserCombinedSerializer.java @@ -0,0 +1,45 @@ +package org.baeldung.jsoncomponent; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.TreeNode; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.node.TextNode; +import javafx.scene.paint.Color; +import org.springframework.boot.jackson.JsonComponent; + +import java.io.IOException; + +@JsonComponent +public class UserCombinedSerializer { + public static class UserJsonSerializer extends JsonSerializer { + + @Override + public void serialize(User user, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException { + jsonGenerator.writeStartObject(); + jsonGenerator.writeStringField("favoriteColor", + getColorAsWebColor(user.getFavoriteColor())); + jsonGenerator.writeEndObject(); + } + + private static String getColorAsWebColor(Color color) { + int r = (int) Math.round(color.getRed() * 255.0); + int g = (int) Math.round(color.getGreen() * 255.0); + int b = (int) Math.round(color.getBlue() * 255.0); + return String.format("#%02x%02x%02x", r, g, b); + } + } + + public static class UserJsonDeserializer extends JsonDeserializer { + @Override + public User deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { + TreeNode treeNode = jsonParser.getCodec().readTree(jsonParser); + TextNode favoriteColor = (TextNode) treeNode.get("favoriteColor"); + return new User(Color.web(favoriteColor.asText())); + } + } +} diff --git a/spring-boot/src/main/java/org/baeldung/jsoncomponent/UserJsonDeserializer.java b/spring-boot/src/main/java/org/baeldung/jsoncomponent/UserJsonDeserializer.java new file mode 100644 index 000000000000..d18de7e3f1cf --- /dev/null +++ b/spring-boot/src/main/java/org/baeldung/jsoncomponent/UserJsonDeserializer.java @@ -0,0 +1,22 @@ +package org.baeldung.jsoncomponent; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.TreeNode; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.node.TextNode; +import javafx.scene.paint.Color; +import org.springframework.boot.jackson.JsonComponent; + +import java.io.IOException; + +@JsonComponent +public class UserJsonDeserializer extends JsonDeserializer { + @Override + public User deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { + TreeNode treeNode = jsonParser.getCodec().readTree(jsonParser); + TextNode favoriteColor = (TextNode) treeNode.get("favoriteColor"); + return new User(Color.web(favoriteColor.asText())); + } +} diff --git a/spring-boot/src/main/java/org/baeldung/jsoncomponent/UserJsonSerializer.java b/spring-boot/src/main/java/org/baeldung/jsoncomponent/UserJsonSerializer.java new file mode 100644 index 000000000000..d90f662a4b1c --- /dev/null +++ b/spring-boot/src/main/java/org/baeldung/jsoncomponent/UserJsonSerializer.java @@ -0,0 +1,29 @@ +package org.baeldung.jsoncomponent; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import javafx.scene.paint.Color; +import org.springframework.boot.jackson.JsonComponent; + +import java.io.IOException; + +@JsonComponent +public class UserJsonSerializer extends JsonSerializer { + + @Override + public void serialize(User user, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException { + jsonGenerator.writeStartObject(); + jsonGenerator.writeStringField("favoriteColor", + getColorAsWebColor(user.getFavoriteColor())); + jsonGenerator.writeEndObject(); + } + + private static String getColorAsWebColor(Color color) { + int r = (int) Math.round(color.getRed() * 255.0); + int g = (int) Math.round(color.getGreen() * 255.0); + int b = (int) Math.round(color.getBlue() * 255.0); + return String.format("#%02x%02x%02x", r, g, b); + } +} diff --git a/spring-boot/src/main/java/org/baeldung/main/SpringBootApplication.java b/spring-boot/src/main/java/org/baeldung/main/SpringBootApplication.java index 582d2d9e9cf9..872426d85054 100644 --- a/spring-boot/src/main/java/org/baeldung/main/SpringBootApplication.java +++ b/spring-boot/src/main/java/org/baeldung/main/SpringBootApplication.java @@ -14,11 +14,13 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import com.baeldung.autoconfiguration.MySQLAutoconfiguration; + import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @RestController -@EnableAutoConfiguration +@EnableAutoConfiguration(exclude=MySQLAutoconfiguration.class) @ComponentScan({ "org.baeldung.common.error", "org.baeldung.common.error.controller", "org.baeldung.common.properties", "org.baeldung.common.resources", "org.baeldung.endpoints", "org.baeldung.service", "org.baeldung.monitor.jmx", "org.baeldung.service" }) public class SpringBootApplication { diff --git a/spring-boot/src/main/java/org/baeldung/session/exception/Application.java b/spring-boot/src/main/java/org/baeldung/session/exception/Application.java index 23d741b98c5a..9f8dadbe55c7 100644 --- a/spring-boot/src/main/java/org/baeldung/session/exception/Application.java +++ b/spring-boot/src/main/java/org/baeldung/session/exception/Application.java @@ -7,8 +7,10 @@ import org.springframework.context.annotation.Bean; import org.springframework.orm.jpa.vendor.HibernateJpaSessionFactoryBean; +import com.baeldung.autoconfiguration.MySQLAutoconfiguration; + @EntityScan(basePackageClasses = Foo.class) -@SpringBootApplication +@SpringBootApplication(exclude=MySQLAutoconfiguration.class) public class Application { public static void main(String[] args) { System.setProperty("spring.config.name", "exception"); diff --git a/spring-boot/src/main/resources/META-INF/spring.factories b/spring-boot/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000000..5f55544eff88 --- /dev/null +++ b/spring-boot/src/main/resources/META-INF/spring.factories @@ -0,0 +1,3 @@ +org.springframework.boot.diagnostics.FailureAnalyzer=com.baeldung.failureanalyzer.MyBeanNotOfRequiredTypeFailureAnalyzer + +org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.baeldung.autoconfiguration.MySQLAutoconfiguration \ No newline at end of file diff --git a/spring-boot/src/main/resources/application.properties b/spring-boot/src/main/resources/application.properties index d30045d1dc35..444f68d50adc 100644 --- a/spring-boot/src/main/resources/application.properties +++ b/spring-boot/src/main/resources/application.properties @@ -2,7 +2,9 @@ server.port=8080 server.contextPath=/springbootapp management.port=8081 management.address=127.0.0.1 - +#debug=true +spring.jpa.show-sql=true +spring.jpa.hibernate.ddl-auto = update endpoints.shutdown.enabled=true endpoints.jmx.domain=Spring Sample Application @@ -22,10 +24,25 @@ http.mappers.jsonPrettyPrint=true info.app.name=Spring Sample Application info.app.description=This is my first spring boot application G1 info.app.version=1.0.0 +info.java-vendor = ${java.specification.vendor} ## Spring Security Configurations security.user.name=admin1 security.user.password=secret1 management.security.role=SUPERUSER -logging.level.org.springframework=INFO \ No newline at end of file +logging.level.org.springframework=INFO + +#Servlet Configuration +servlet.name=dispatcherExample +servlet.mapping=/dispatcherExampleURL + +#banner.charset=UTF-8 +#banner.location=classpath:banner.txt +#banner.image.location=classpath:banner.gif +#banner.image.width= //TODO +#banner.image.height= //TODO +#banner.image.margin= //TODO +#banner.image.invert= //TODO + +contactInfoType=email \ No newline at end of file diff --git a/spring-boot/src/main/resources/banner.txt b/spring-boot/src/main/resources/banner.txt new file mode 100644 index 000000000000..abfa666eb66f --- /dev/null +++ b/spring-boot/src/main/resources/banner.txt @@ -0,0 +1,14 @@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@@#@@@@@########@@@@@@@@@@@@@@@@@@@@@@@@...@@@@@@@@@:..@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@#. @@@@@* *@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@. @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@#o @@@@@* @@@@@* @@@:*.*@@@@@@@: *8@@@ @@@@&:.#@. @o**@@@@**:@o*o@@:.:@@@@@:.o#@&*:@@@@ +@@@@@@@@@@@@* @@@@@* 8888 8@ @@@8 #@o 8@# .@ @@* :. @* @@@@ @. : &@ ** .@@@@ +@@@@@@@@@@. @ o@@@@@* *@@@o::& .* 8@@@@. @@ 8@@@@. @* @@@@ @. @@@& * @@@@# .@@@@ +@@@@@@@@@& @ @@@@@@* @@@@@@ 8 @@@@ .. o&&&&&&& @@ #@@@@. @* @@@@ @. @@@# * @@@@@ .@@@@ +@@@@@@@@@ @@o @@@@@@@* oooo* 8 @@@& @* @@@ # 88. 88. *& o#: @. @@@# *@ &#& .@@@@ +@@@@@@@@# @@@8 @@@@@@@* .*@@@#. *@@ @@@& :#@@@o .@@: *&@8 @o o@@: @. @@@# *@@#. :8# .@@@@ +@@@@@@@@@ @@@@ &@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@# o@@@@ @@@@@ +@@@@@& &@@@@ 8@@@@@@@@@8&8@@@@@#8#@@@o8@#&@@o&@@@&@@8@@&@@@@88@@8#@8&@@##@@@@@@#8@@#8@@88@@@@@ *@@@@@@@ +@@@# #@@@@#. @@@@@@@@@@@@@8@@8#o@&#@@@@o.@o*@@*.@@@.@&:8o8*@@@8&@@#@@@8@@@@8@#@@@8&@@@@@@#@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ \ No newline at end of file diff --git a/spring-boot-servlet/src/main/resources/custom.properties b/spring-boot/src/main/resources/custom.properties similarity index 100% rename from spring-boot-servlet/src/main/resources/custom.properties rename to spring-boot/src/main/resources/custom.properties diff --git a/spring-boot/src/main/resources/data-expressions.sql b/spring-boot/src/main/resources/data-expressions.sql new file mode 100644 index 000000000000..3e702a759d0d --- /dev/null +++ b/spring-boot/src/main/resources/data-expressions.sql @@ -0,0 +1,3 @@ +insert into contact_info_expression values ('email','[a-z0-9!#$%&*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?') +insert into contact_info_expression values ('phone','^([0-9]( |-)?)?(\(?[0-9]{3}\)?|[0-9]{3})( |-)?([0-9]{3}( |-)?[0-9]{4}|[a-zA-Z0-9]{7})$') +insert into contact_info_expression values ('website','^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$') \ No newline at end of file diff --git a/spring-boot/src/main/resources/data.sql b/spring-boot/src/main/resources/data.sql new file mode 100644 index 000000000000..c44034c7399a --- /dev/null +++ b/spring-boot/src/main/resources/data.sql @@ -0,0 +1,5 @@ +insert into users values (1, 'Alex', 1); +insert into users values (2, 'Bob', 1); +insert into users values (3, 'John', 0); +insert into users values (4, 'Harry', 0); +insert into users values (5, 'Smith', 1); \ No newline at end of file diff --git a/spring-boot/src/main/resources/logback.xml b/spring-boot/src/main/resources/logback.xml index 78913ee76f6a..ec0dc2469ae0 100644 --- a/spring-boot/src/main/resources/logback.xml +++ b/spring-boot/src/main/resources/logback.xml @@ -1,14 +1,19 @@ + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - + + - - - + + + + + \ No newline at end of file diff --git a/spring-boot/src/main/resources/messages.properties b/spring-boot/src/main/resources/messages.properties new file mode 100644 index 000000000000..e4dbc44c3f5e --- /dev/null +++ b/spring-boot/src/main/resources/messages.properties @@ -0,0 +1,4 @@ +greeting=Hello! Welcome to our website! +lang.change=Change the language +lang.eng=English +lang.fr=French \ No newline at end of file diff --git a/spring-boot/src/main/resources/messages_fr.properties b/spring-boot/src/main/resources/messages_fr.properties new file mode 100644 index 000000000000..ac5853717d5d --- /dev/null +++ b/spring-boot/src/main/resources/messages_fr.properties @@ -0,0 +1,4 @@ +greeting=Bonjour! Bienvenue sur notre site! +lang.change=Changez la langue +lang.eng=Anglais +lang.fr=Francais \ No newline at end of file diff --git a/spring-boot/src/main/resources/mysql.properties b/spring-boot/src/main/resources/mysql.properties new file mode 100644 index 000000000000..27092f852f64 --- /dev/null +++ b/spring-boot/src/main/resources/mysql.properties @@ -0,0 +1,5 @@ +usemysql=local + +mysql-hibernate.dialect=org.hibernate.dialect.MySQLDialect +mysql-hibernate.show_sql=true +mysql-hibernate.hbm2ddl.auto=create-drop \ No newline at end of file diff --git a/spring-boot/src/main/resources/schema-expressions.sql b/spring-boot/src/main/resources/schema-expressions.sql new file mode 100644 index 000000000000..59f6ab05ebbc --- /dev/null +++ b/spring-boot/src/main/resources/schema-expressions.sql @@ -0,0 +1,5 @@ +create table contact_info_expression( + expression_type varchar(50) not null, + pattern varchar(500) not null, + PRIMARY KEY ( expression_type ) +); \ No newline at end of file diff --git a/spring-boot/src/main/resources/schema.sql b/spring-boot/src/main/resources/schema.sql new file mode 100644 index 000000000000..4cd345c762a0 --- /dev/null +++ b/spring-boot/src/main/resources/schema.sql @@ -0,0 +1,8 @@ +drop table if exists USERS; + +create table USERS( + ID int not null AUTO_INCREMENT, + NAME varchar(100) not null, + STATUS int, + PRIMARY KEY ( ID ) +); \ No newline at end of file diff --git a/spring-boot/src/main/resources/templates/customer.html b/spring-boot/src/main/resources/templates/customer.html new file mode 100644 index 000000000000..c8f5a25d5e30 --- /dev/null +++ b/spring-boot/src/main/resources/templates/customer.html @@ -0,0 +1,16 @@ + + + +Customer Page + + + +
+
+Contact Info:
+ +
+

+
+ + \ No newline at end of file diff --git a/spring-boot/src/main/resources/templates/international.html b/spring-boot/src/main/resources/templates/international.html new file mode 100644 index 000000000000..a2a5fbb5910e --- /dev/null +++ b/spring-boot/src/main/resources/templates/international.html @@ -0,0 +1,29 @@ + + + + +Home + + + + +

+ +

+: + + + \ No newline at end of file diff --git a/spring-boot-servlet/src/main/webapp/WEB-INF/context.xml b/spring-boot/src/main/webapp/WEB-INF/context.xml similarity index 100% rename from spring-boot-servlet/src/main/webapp/WEB-INF/context.xml rename to spring-boot/src/main/webapp/WEB-INF/context.xml diff --git a/spring-boot-servlet/src/main/webapp/WEB-INF/dispatcher.xml b/spring-boot/src/main/webapp/WEB-INF/dispatcher.xml similarity index 100% rename from spring-boot-servlet/src/main/webapp/WEB-INF/dispatcher.xml rename to spring-boot/src/main/webapp/WEB-INF/dispatcher.xml diff --git a/spring-boot-servlet/src/main/webapp/WEB-INF/web.xml b/spring-boot/src/main/webapp/WEB-INF/web.xml similarity index 100% rename from spring-boot-servlet/src/main/webapp/WEB-INF/web.xml rename to spring-boot/src/main/webapp/WEB-INF/web.xml diff --git a/spring-boot-servlet/src/main/webapp/annotationservlet.jsp b/spring-boot/src/main/webapp/annotationservlet.jsp similarity index 100% rename from spring-boot-servlet/src/main/webapp/annotationservlet.jsp rename to spring-boot/src/main/webapp/annotationservlet.jsp diff --git a/spring-boot-servlet/src/main/webapp/index.jsp b/spring-boot/src/main/webapp/index.jsp similarity index 100% rename from spring-boot-servlet/src/main/webapp/index.jsp rename to spring-boot/src/main/webapp/index.jsp diff --git a/spring-boot/src/test/java/com/baeldung/autoconfiguration/AutoconfigurationTest.java b/spring-boot/src/test/java/com/baeldung/autoconfiguration/AutoconfigurationTest.java new file mode 100644 index 000000000000..608d0d10bc6b --- /dev/null +++ b/spring-boot/src/test/java/com/baeldung/autoconfiguration/AutoconfigurationTest.java @@ -0,0 +1,28 @@ +package com.baeldung.autoconfiguration; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.baeldung.autoconfiguration.example.AutoconfigurationApplication; +import com.baeldung.autoconfiguration.example.MyUser; +import com.baeldung.autoconfiguration.example.MyUserRepository; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest(classes = AutoconfigurationApplication.class) +@EnableJpaRepositories(basePackages = { "com.baeldung.autoconfiguration.example" }) +public class AutoconfigurationTest { + + @Autowired + private MyUserRepository userRepository; + + @Test + public void whenSaveUser_thenOk() { + MyUser user = new MyUser("user@email.com"); + userRepository.save(user); + } + +} diff --git a/spring-boot/src/test/java/org/baeldung/SpringBootApplicationIntegrationTest.java b/spring-boot/src/test/java/org/baeldung/SpringBootApplicationIntegrationTest.java index 87c59a4662e2..4ce0678bdd5b 100644 --- a/spring-boot/src/test/java/org/baeldung/SpringBootApplicationIntegrationTest.java +++ b/spring-boot/src/test/java/org/baeldung/SpringBootApplicationIntegrationTest.java @@ -63,4 +63,4 @@ public void givenRequestHasBeenMade_whenMeetsFindByVersionOfGivenConditions_then mockMvc.perform(MockMvcRequestBuilders.get("/entity/findbyversion").header("Version", "1.0.0")).andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.content().contentType(contentType)) .andExpect(jsonPath("$.id", equalTo(1))); } -} +} \ No newline at end of file diff --git a/spring-boot/src/test/java/org/baeldung/SpringBootH2IntegrationTest.java b/spring-boot/src/test/java/org/baeldung/SpringBootH2IntegrationTest.java new file mode 100644 index 000000000000..185a36e57121 --- /dev/null +++ b/spring-boot/src/test/java/org/baeldung/SpringBootH2IntegrationTest.java @@ -0,0 +1,28 @@ +package org.baeldung; + +import org.baeldung.config.H2JpaConfig; +import org.baeldung.domain.GenericEntity; +import org.baeldung.repository.GenericEntityRepository; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest(classes = { Application.class, H2JpaConfig.class }) +public class SpringBootH2IntegrationTest { + @Autowired + private GenericEntityRepository genericEntityRepository; + + @Test + public void givenGenericEntityRepository_whenSaveAndRetreiveEntity_thenOK() { + GenericEntity genericEntity = genericEntityRepository.save(new GenericEntity("test")); + GenericEntity foundEntity = genericEntityRepository.findOne(genericEntity.getId()); + assertNotNull(foundEntity); + assertEquals(genericEntity.getValue(), foundEntity.getValue()); + } +} \ No newline at end of file diff --git a/spring-boot/src/test/java/org/baeldung/SpringBootJPAIntegrationTest.java b/spring-boot/src/test/java/org/baeldung/SpringBootJPAIntegrationTest.java index d4b19e6a1db0..202d24ffc791 100644 --- a/spring-boot/src/test/java/org/baeldung/SpringBootJPAIntegrationTest.java +++ b/spring-boot/src/test/java/org/baeldung/SpringBootJPAIntegrationTest.java @@ -20,8 +20,8 @@ public class SpringBootJPAIntegrationTest { @Test public void givenGenericEntityRepository_whenSaveAndRetreiveEntity_thenOK() { GenericEntity genericEntity = genericEntityRepository.save(new GenericEntity("test")); - GenericEntity foundedEntity = genericEntityRepository.findOne(genericEntity.getId()); - assertNotNull(foundedEntity); - assertEquals(genericEntity.getValue(), foundedEntity.getValue()); + GenericEntity foundEntity = genericEntityRepository.findOne(genericEntity.getId()); + assertNotNull(foundEntity); + assertEquals(genericEntity.getValue(), foundEntity.getValue()); } } diff --git a/spring-boot/src/test/java/org/baeldung/SpringBootProfileIntegrationTest.java b/spring-boot/src/test/java/org/baeldung/SpringBootProfileIntegrationTest.java new file mode 100644 index 000000000000..806b38a8ce87 --- /dev/null +++ b/spring-boot/src/test/java/org/baeldung/SpringBootProfileIntegrationTest.java @@ -0,0 +1,30 @@ +package org.baeldung; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.baeldung.config.H2TestProfileJPAConfig; +import org.baeldung.domain.GenericEntity; +import org.baeldung.repository.GenericEntityRepository; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest(classes = { Application.class, H2TestProfileJPAConfig.class }) +@ActiveProfiles("test") +public class SpringBootProfileIntegrationTest { + @Autowired + private GenericEntityRepository genericEntityRepository; + + @Test + public void givenGenericEntityRepository_whenSaveAndRetreiveEntity_thenOK() { + GenericEntity genericEntity = genericEntityRepository.save(new GenericEntity("test")); + GenericEntity foundEntity = genericEntityRepository.findOne(genericEntity.getId()); + assertNotNull(foundEntity); + assertEquals(genericEntity.getValue(), foundEntity.getValue()); + } +} diff --git a/spring-boot/src/test/java/org/baeldung/boot/boottest/EmployeeControllerIntegrationTest.java b/spring-boot/src/test/java/org/baeldung/boot/boottest/EmployeeControllerIntegrationTest.java new file mode 100644 index 000000000000..2146fc09bc56 --- /dev/null +++ b/spring-boot/src/test/java/org/baeldung/boot/boottest/EmployeeControllerIntegrationTest.java @@ -0,0 +1,75 @@ +package org.baeldung.boot.boottest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.internal.verification.VerificationModeFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; + +import java.util.Arrays; +import java.util.List; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.Matchers.hasSize; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +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.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@RunWith(SpringRunner.class) +@WebMvcTest(EmployeeRestController.class) +public class EmployeeControllerIntegrationTest { + + @Autowired + private MockMvc mvc; + + @MockBean + private EmployeeService service; + + @Before + public void setUp() throws Exception { + } + + @Test + public void whenPostEmployee_thenCreateEmployee() throws Exception { + Employee alex = new Employee("alex"); + given(service.save(Mockito.anyObject())).willReturn(alex); + + mvc.perform(post("/api/employees").contentType(MediaType.APPLICATION_JSON) + .content(JsonUtil.toJson(alex))) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$.name", is("alex"))); + verify(service, VerificationModeFactory.times(1)).save(Mockito.anyObject()); + reset(service); + } + + @Test + public void givenEmployees_whenGetEmployees_thenReturnJsonArray() throws Exception { + Employee alex = new Employee("alex"); + Employee john = new Employee("john"); + Employee bob = new Employee("bob"); + + List allEmployees = Arrays.asList(alex, john, bob); + + given(service.getAllEmployees()).willReturn(allEmployees); + + mvc.perform(get("/api/employees").contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(3))) + .andExpect(jsonPath("$[0].name", is(alex.getName()))) + .andExpect(jsonPath("$[1].name", is(john.getName()))) + .andExpect(jsonPath("$[2].name", is(bob.getName()))); + verify(service, VerificationModeFactory.times(1)).getAllEmployees(); + reset(service); + } + +} \ No newline at end of file diff --git a/spring-boot/src/test/java/org/baeldung/boot/boottest/EmployeeRepositoryIntegrationTest.java b/spring-boot/src/test/java/org/baeldung/boot/boottest/EmployeeRepositoryIntegrationTest.java new file mode 100644 index 000000000000..cf94f4be4929 --- /dev/null +++ b/spring-boot/src/test/java/org/baeldung/boot/boottest/EmployeeRepositoryIntegrationTest.java @@ -0,0 +1,78 @@ +package org.baeldung.boot.boottest; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Optional; + +import org.baeldung.boot.boottest.Employee; +import org.baeldung.boot.boottest.EmployeeRepository; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@DataJpaTest +public class EmployeeRepositoryIntegrationTest { + + @Autowired + private TestEntityManager entityManager; + + @Autowired + private EmployeeRepository employeeRepository; + + @Test + public void whenFindByName_thenReturnEmployee() { + Employee emp = new Employee("test"); + entityManager.persistAndFlush(emp); + + Optional fromDb = employeeRepository.findByName(emp.getName()); + assertThat(fromDb.get() + .getName()).isEqualTo(emp.getName()); + } + + @Test(expected = NoSuchElementException.class) + public void whenInvalidName_thenNoSuchElementException() { + Optional fromDb = employeeRepository.findByName("doesNotExist"); + fromDb.get(); + } + + @Test + public void whenFindById_thenReturnEmployee() { + Employee emp = new Employee("test"); + entityManager.persistAndFlush(emp); + + Optional fromDb = employeeRepository.findById(emp.getId()); + assertThat(fromDb.get() + .getName()).isEqualTo(emp.getName()); + } + + @Test(expected = NoSuchElementException.class) + public void whenInvalidId_thenNoSuchElementException() { + Optional fromDb = employeeRepository.findById(-11L); + fromDb.get(); + } + + @Test + public void givenSetOfEmployees_whenFindAll_thenReturnAllEmployees() { + Employee alex = new Employee("alex"); + Employee ron = new Employee("ron"); + Employee bob = new Employee("bob"); + + entityManager.persist(alex); + entityManager.persist(bob); + entityManager.persist(ron); + entityManager.flush(); + + List allEmployees = employeeRepository.findAll(); + + assertThat(allEmployees).hasSize(3) + .extracting(Employee::getName) + .containsOnly(alex.getName(), ron.getName(), bob.getName()); + } + +} diff --git a/spring-boot/src/test/java/org/baeldung/boot/boottest/EmployeeRestControllerIntegrationTest.java b/spring-boot/src/test/java/org/baeldung/boot/boottest/EmployeeRestControllerIntegrationTest.java new file mode 100644 index 000000000000..110092d2931c --- /dev/null +++ b/spring-boot/src/test/java/org/baeldung/boot/boottest/EmployeeRestControllerIntegrationTest.java @@ -0,0 +1,78 @@ +package org.baeldung.boot.boottest; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.hasSize; +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.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.io.IOException; +import java.util.List; + +import org.baeldung.boot.DemoApplication; +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.http.MediaType; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = DemoApplication.class) +@AutoConfigureMockMvc +@TestPropertySource(locations = "classpath:application-integrationtest.properties") +public class EmployeeRestControllerIntegrationTest { + + @Autowired + private MockMvc mvc; + + @Autowired + private EmployeeRepository repository; + + @After + public void resetDb() { + repository.deleteAll(); + } + + @Test + public void whenValidInput_thenCreateEmployee() throws IOException, Exception { + Employee bob = new Employee("bob"); + mvc.perform(post("/api/employees").contentType(MediaType.APPLICATION_JSON) + .content(JsonUtil.toJson(bob))); + + List found = repository.findAll(); + assertThat(found).extracting(Employee::getName) + .containsOnly("bob"); + } + + @Test + public void givenEmployees_whenGetEmployees_thenStatus200() throws Exception { + + createTestEmployee("bob"); + createTestEmployee("alex"); + + mvc.perform(get("/api/employees").contentType(MediaType.APPLICATION_JSON)) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$", hasSize(greaterThanOrEqualTo(2)))) + .andExpect(jsonPath("$[0].name", is("bob"))) + .andExpect(jsonPath("$[1].name", is("alex"))); + } + + private void createTestEmployee(String name) { + Employee emp = new Employee(name); + repository.saveAndFlush(emp); + } + +} diff --git a/spring-boot/src/test/java/org/baeldung/boot/boottest/EmployeeServiceImplIntegrationTest.java b/spring-boot/src/test/java/org/baeldung/boot/boottest/EmployeeServiceImplIntegrationTest.java new file mode 100644 index 000000000000..e8a585a23e22 --- /dev/null +++ b/spring-boot/src/test/java/org/baeldung/boot/boottest/EmployeeServiceImplIntegrationTest.java @@ -0,0 +1,145 @@ +package org.baeldung.boot.boottest; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Arrays; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Optional; + +import org.baeldung.boot.boottest.Employee; +import org.baeldung.boot.boottest.EmployeeRepository; +import org.baeldung.boot.boottest.EmployeeService; +import org.baeldung.boot.boottest.EmployeeServiceImpl; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.internal.verification.VerificationModeFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Bean; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +public class EmployeeServiceImplIntegrationTest { + + @TestConfiguration + static class EmployeeServiceImplTestContextConfiguration { + @Bean + public EmployeeService employeeService() { + return new EmployeeServiceImpl(); + } + } + + @Autowired + private EmployeeService employeeService; + + @MockBean + private EmployeeRepository employeeRepository; + + @Before + public void setUp() { + Employee john = new Employee("john"); + john.setId(11L); + + Optional emp = Optional.of(john); + + Employee bob = new Employee("bob"); + Employee alex = new Employee("alex"); + + List allEmployees = Arrays.asList(john, bob, alex); + + Mockito.when(employeeRepository.findByName(john.getName())) + .thenReturn(emp); + Mockito.when(employeeRepository.findByName("wrong_name")) + .thenReturn(Optional.empty()); + Mockito.when(employeeRepository.findById(john.getId())) + .thenReturn(emp); + Mockito.when(employeeRepository.findAll()) + .thenReturn(allEmployees); + Mockito.when(employeeRepository.findById(-99L)) + .thenReturn(Optional.empty()); + } + + @Test + public void whenValidName_thenEmployeeShouldBeFound() { + Optional fromDb = employeeService.getEmployeeByName("john"); + assertThat(fromDb.get() + .getName()).isEqualTo("john"); + + verifyFindByNameIsCalledOnce("john"); + } + + @Test(expected = NoSuchElementException.class) + public void whenInValidName_thenEmployeeShouldNotBeFound() { + Optional fromDb = employeeService.getEmployeeByName("wrong_name"); + fromDb.get(); + + verifyFindByNameIsCalledOnce("wrong_name"); + } + + @Test + public void whenValidName_thenEmployeeShouldExist() { + boolean doesEmployeeExist = employeeService.exists("john"); + assertThat(doesEmployeeExist).isEqualTo(true); + + verifyFindByNameIsCalledOnce("john"); + } + + @Test + public void whenNonExistingName_thenEmployeeShouldNotExist() { + boolean doesEmployeeExist = employeeService.exists("some_name"); + assertThat(doesEmployeeExist).isEqualTo(false); + + verifyFindByNameIsCalledOnce("some_name"); + } + + @Test + public void whenValidI_thendEmployeeShouldBeFound() { + Optional fromDb = employeeService.getEmployeeById(11L); + assertThat(fromDb.get() + .getName()).isEqualTo("john"); + + verifyFindByIdIsCalledOnce(); + } + + @Test(expected = NoSuchElementException.class) + public void whenInValidId_thenEmployeeShouldNotBeFound() { + Optional fromDb = employeeService.getEmployeeById(-99L); + verifyFindByIdIsCalledOnce(); + fromDb.get(); + } + + @Test + public void given3Employees_whengetAll_thenReturn3Records() { + Employee alex = new Employee("alex"); + Employee john = new Employee("john"); + Employee bob = new Employee("bob"); + + List allEmployees = employeeService.getAllEmployees(); + verifyFindAllEmployeesIsCalledOnce(); + assertThat(allEmployees).hasSize(3) + .extracting(Employee::getName) + .contains(alex.getName(), john.getName(), bob.getName()); + } + + private void verifyFindByNameIsCalledOnce(String name) { + Mockito.verify(employeeRepository, VerificationModeFactory.times(1)) + .findByName(name); + Mockito.reset(employeeRepository); + } + + private void verifyFindByIdIsCalledOnce() { + Mockito.verify(employeeRepository, VerificationModeFactory.times(1)) + .findById(Mockito.anyLong()); + Mockito.reset(employeeRepository); + } + + private void verifyFindAllEmployeesIsCalledOnce() { + Mockito.verify(employeeRepository, VerificationModeFactory.times(1)) + .findAll(); + Mockito.reset(employeeRepository); + } +} diff --git a/spring-boot/src/test/java/org/baeldung/boot/boottest/JsonUtil.java b/spring-boot/src/test/java/org/baeldung/boot/boottest/JsonUtil.java new file mode 100644 index 000000000000..3d532ce54afb --- /dev/null +++ b/spring-boot/src/test/java/org/baeldung/boot/boottest/JsonUtil.java @@ -0,0 +1,14 @@ +package org.baeldung.boot.boottest; + +import java.io.IOException; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class JsonUtil { + public static byte[] toJson(Object object) throws IOException { + ObjectMapper mapper = new ObjectMapper(); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + return mapper.writeValueAsBytes(object); + } +} diff --git a/spring-boot/src/test/java/org/baeldung/config/H2TestProfileJPAConfig.java b/spring-boot/src/test/java/org/baeldung/config/H2TestProfileJPAConfig.java new file mode 100644 index 000000000000..1d696f4a5d10 --- /dev/null +++ b/spring-boot/src/test/java/org/baeldung/config/H2TestProfileJPAConfig.java @@ -0,0 +1,66 @@ +package org.baeldung.config; + +import java.util.Properties; + +import javax.persistence.EntityManagerFactory; +import javax.sql.DataSource; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +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.repository", "org.baeldung.boot.repository" }) +@EnableTransactionManagement +public class H2TestProfileJPAConfig { + + @Autowired + private Environment env; + + @Bean + @Profile("test") + public DataSource dataSource() { + final DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName("org.h2.Driver"); + dataSource.setUrl("jdbc:h2:mem:db;DB_CLOSE_DELAY=-1"); + dataSource.setUsername("sa"); + dataSource.setPassword("sa"); + + return dataSource; + } + + @Bean + public LocalContainerEntityManagerFactoryBean entityManagerFactory() { + final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); + em.setDataSource(dataSource()); + em.setPackagesToScan(new String[] { "org.baeldung.domain", "org.baeldung.boot.model" }); + em.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); + em.setJpaProperties(additionalProperties()); + return em; + } + + @Bean + JpaTransactionManager transactionManager(final EntityManagerFactory entityManagerFactory) { + final 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")); + + return hibernateProperties; + } +} diff --git a/spring-boot/src/test/java/org/baeldung/jsoncomponent/UserJsonDeserializerIntegrationTest.java b/spring-boot/src/test/java/org/baeldung/jsoncomponent/UserJsonDeserializerIntegrationTest.java new file mode 100644 index 000000000000..4f5af3d0e78f --- /dev/null +++ b/spring-boot/src/test/java/org/baeldung/jsoncomponent/UserJsonDeserializerIntegrationTest.java @@ -0,0 +1,27 @@ +package org.baeldung.jsoncomponent; + +import com.fasterxml.jackson.databind.ObjectMapper; +import javafx.scene.paint.Color; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.json.JsonTest; +import org.springframework.test.context.junit4.SpringRunner; + +import java.io.IOException; + +import static org.junit.Assert.assertEquals; + +@JsonTest +@RunWith(SpringRunner.class) +public class UserJsonDeserializerIntegrationTest { + + @Autowired + private ObjectMapper objectMapper; + + @Test + public void testDeserialize() throws IOException { + User user = objectMapper.readValue("{\"favoriteColor\":\"#f0f8ff\"}", User.class); + assertEquals(Color.ALICEBLUE, user.getFavoriteColor()); + } +} \ No newline at end of file diff --git a/spring-boot/src/test/java/org/baeldung/jsoncomponent/UserJsonSerializerIntegrationTest.java b/spring-boot/src/test/java/org/baeldung/jsoncomponent/UserJsonSerializerIntegrationTest.java new file mode 100644 index 000000000000..c1b4c8912c24 --- /dev/null +++ b/spring-boot/src/test/java/org/baeldung/jsoncomponent/UserJsonSerializerIntegrationTest.java @@ -0,0 +1,27 @@ +package org.baeldung.jsoncomponent; + + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import javafx.scene.paint.Color; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.json.JsonTest; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.junit.Assert.assertEquals; + +@JsonTest +@RunWith(SpringRunner.class) +public class UserJsonSerializerIntegrationTest { + + @Autowired + private ObjectMapper objectMapper; + + @Test + public void testSerialization() throws JsonProcessingException { + String json = objectMapper.writeValueAsString(new User(Color.ALICEBLUE)); + assertEquals("{\"favoriteColor\":\"#f0f8ff\"}", json); + } +} \ No newline at end of file diff --git a/spring-boot/src/test/resources/application-integrationtest.properties b/spring-boot/src/test/resources/application-integrationtest.properties new file mode 100644 index 000000000000..508acf45e069 --- /dev/null +++ b/spring-boot/src/test/resources/application-integrationtest.properties @@ -0,0 +1,2 @@ +spring.datasource.url = jdbc:h2:mem:test +spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.H2Dialect \ No newline at end of file diff --git a/spring-boot/src/test/resources/application.properties b/spring-boot/src/test/resources/application.properties index 14b190629e16..85e4e6e66fce 100644 --- a/spring-boot/src/test/resources/application.properties +++ b/spring-boot/src/test/resources/application.properties @@ -1,3 +1,19 @@ spring.mail.host=localhost spring.mail.port=8025 -spring.mail.properties.mail.smtp.auth=false \ No newline at end of file +spring.mail.properties.mail.smtp.auth=false + +security.basic.enabled=false + +# spring.datasource.x +spring.datasource.driver-class-name=org.h2.Driver +spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1 +spring.datasource.username=sa +spring.datasource.password=sa + +# hibernate.X +hibernate.dialect=org.hibernate.dialect.H2Dialect +hibernate.show_sql=true +hibernate.hbm2ddl.auto=create-drop +hibernate.cache.use_second_level_cache=true +hibernate.cache.use_query_cache=true +hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.EhCacheRegionFactory \ No newline at end of file diff --git a/spring-boot/src/test/resources/import.sql b/spring-boot/src/test/resources/import.sql new file mode 100644 index 000000000000..9095b9468c5a --- /dev/null +++ b/spring-boot/src/test/resources/import.sql @@ -0,0 +1 @@ +--insert into Foo values(1,'Foo_Name'); \ No newline at end of file diff --git a/spring-boot/src/test/resources/org/baeldung/boot/expected.json b/spring-boot/src/test/resources/org/baeldung/boot/expected.json new file mode 100644 index 000000000000..f5409421a62d --- /dev/null +++ b/spring-boot/src/test/resources/org/baeldung/boot/expected.json @@ -0,0 +1,4 @@ +{ + "id":3, + "name":"Foo_Name_3" +} \ No newline at end of file diff --git a/spring-boot/src/test/resources/persistence-generic-entity.properties b/spring-boot/src/test/resources/persistence-generic-entity.properties new file mode 100644 index 000000000000..b19304cb1f62 --- /dev/null +++ b/spring-boot/src/test/resources/persistence-generic-entity.properties @@ -0,0 +1,8 @@ +jdbc.driverClassName=org.h2.Driver +jdbc.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1 +jdbc.user=sa +jdbc.pass=sa + +hibernate.dialect=org.hibernate.dialect.H2Dialect +hibernate.show_sql=true +hibernate.hbm2ddl.auto=create-drop diff --git a/spring-cloud-data-flow/data-flow-server/pom.xml b/spring-cloud-data-flow/data-flow-server/pom.xml index 1ed2d4fb74ab..bd3b4ab15017 100644 --- a/spring-cloud-data-flow/data-flow-server/pom.xml +++ b/spring-cloud-data-flow/data-flow-server/pom.xml @@ -77,39 +77,4 @@ - - - integration - - - - org.apache.maven.plugins - maven-surefire-plugin - - - integration-test - - test - - - - **/*LiveTest.java - - - **/*IntegrationTest.java - - - - - - - json - - - - - - - - diff --git a/spring-cloud-data-flow/data-flow-shell/pom.xml b/spring-cloud-data-flow/data-flow-shell/pom.xml index d3bd297152ac..350f199d4b98 100644 --- a/spring-cloud-data-flow/data-flow-shell/pom.xml +++ b/spring-cloud-data-flow/data-flow-shell/pom.xml @@ -68,6 +68,8 @@ org.apache.maven.plugins maven-surefire-plugin + 3 + true **/*IntegrationTest.java **/*LiveTest.java diff --git a/spring-cloud/pom.xml b/spring-cloud/pom.xml index 5b5b006d0112..6d53b24647cb 100644 --- a/spring-cloud/pom.xml +++ b/spring-cloud/pom.xml @@ -13,6 +13,7 @@ spring-cloud-bootstrap spring-cloud-ribbon-client spring-cloud-rest + spring-cloud-zookeeper pom diff --git a/spring-cloud/spring-cloud-bootstrap/README.MD b/spring-cloud/spring-cloud-bootstrap/README.MD index d6f8faf31e32..d8eedc32496b 100644 --- a/spring-cloud/spring-cloud-bootstrap/README.MD +++ b/spring-cloud/spring-cloud-bootstrap/README.MD @@ -1,3 +1,14 @@ ### Relevant Articles: - [Spring Cloud – Bootstrapping](http://www.baeldung.com/spring-cloud-bootstrapping) - [Spring Cloud – Securing Services](http://www.baeldung.com/spring-cloud-securing-services) +- [Spring Cloud – Tracing Services with Zipkin](http://www.baeldung.com/tracing-services-with-zipkin) + + +- To run the project: + - copy the appliction-config folder to c:\Users\{username}\ on Windows or /Users/{username}/ on *nix. Then open a git bash terminal in application-config and run: + - git init + - git add . + - git commit -m "First commit" + - start the config server + - start the discover server + - start all the other servers in any order (gateway, svc-book, svc-rating, zipkin) diff --git a/spring-cloud/spring-cloud-bootstrap/application-config/book-service.properties b/spring-cloud/spring-cloud-bootstrap/application-config/book-service.properties index e1244a0cf04d..49f7d1ed913a 100644 --- a/spring-cloud/spring-cloud-bootstrap/application-config/book-service.properties +++ b/spring-cloud/spring-cloud-bootstrap/application-config/book-service.properties @@ -15,3 +15,6 @@ logging.level.org.springframework.security=debug spring.redis.host=localhost spring.redis.port=6379 + +spring.sleuth.sampler.percentage=1.0 +spring.sleuth.web.skipPattern=(^cleanup.*) diff --git a/spring-cloud/spring-cloud-bootstrap/application-config/gateway.properties b/spring-cloud/spring-cloud-bootstrap/application-config/gateway.properties index 09f7f3bf4acd..e9e593284cef 100644 --- a/spring-cloud/spring-cloud-bootstrap/application-config/gateway.properties +++ b/spring-cloud/spring-cloud-bootstrap/application-config/gateway.properties @@ -24,4 +24,7 @@ logging.level.org.springframework.security=debug logging.level.org.springframework.cloud.netflix.zuul=debug spring.redis.host=localhost -spring.redis.port=6379 \ No newline at end of file +spring.redis.port=6379 + +spring.sleuth.sampler.percentage=1.0 +spring.sleuth.web.skipPattern=(^cleanup.*|.+favicon.*) \ No newline at end of file diff --git a/spring-cloud/spring-cloud-bootstrap/application-config/rating-service.properties b/spring-cloud/spring-cloud-bootstrap/application-config/rating-service.properties index 4817d12c83b1..b7cbb6fbd65e 100644 --- a/spring-cloud/spring-cloud-bootstrap/application-config/rating-service.properties +++ b/spring-cloud/spring-cloud-bootstrap/application-config/rating-service.properties @@ -15,3 +15,6 @@ logging.level.org.springframework.security=debug spring.redis.host=localhost spring.redis.port=6379 + +spring.sleuth.sampler.percentage=1.0 +spring.sleuth.web.skipPattern=(^cleanup.*) diff --git a/spring-cloud/spring-cloud-bootstrap/application-config/zipkin.properties b/spring-cloud/spring-cloud-bootstrap/application-config/zipkin.properties new file mode 100644 index 000000000000..ca3aed2263e0 --- /dev/null +++ b/spring-cloud/spring-cloud-bootstrap/application-config/zipkin.properties @@ -0,0 +1,7 @@ +spring.application.name=zipkin +server.port=9411 + +eureka.client.region = default +eureka.client.registryFetchIntervalSeconds = 5 + +logging.level.org.springframework.web=debug diff --git a/spring-cloud/spring-cloud-bootstrap/gateway/pom.xml b/spring-cloud/spring-cloud-bootstrap/gateway/pom.xml index 044730ba22cd..13ae6b40508a 100644 --- a/spring-cloud/spring-cloud-bootstrap/gateway/pom.xml +++ b/spring-cloud/spring-cloud-bootstrap/gateway/pom.xml @@ -41,11 +41,23 @@ spring-boot-starter-data-redis
+ + org.springframework.cloud + spring-cloud-starter-zipkin + + org.springframework.boot spring-boot-starter-test test + + + io.rest-assured + rest-assured + test + 3.0.2 + diff --git a/spring-cloud/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/GatewayApplication.java b/spring-cloud/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/GatewayApplication.java index b5ae1e4e7b8b..16aae66cab4c 100644 --- a/spring-cloud/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/GatewayApplication.java +++ b/spring-cloud/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/GatewayApplication.java @@ -1,6 +1,9 @@ package com.baeldung.spring.cloud.bootstrap.gateway; +import com.netflix.appinfo.InstanceInfo; +import com.netflix.discovery.EurekaClient; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.loadbalancer.LoadBalanced; @@ -8,8 +11,13 @@ import org.springframework.cloud.netflix.ribbon.RibbonClientSpecification; import org.springframework.cloud.netflix.ribbon.SpringClientFactory; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; +import org.springframework.cloud.sleuth.metric.SpanMetricReporter; +import org.springframework.cloud.sleuth.zipkin.HttpZipkinSpanReporter; +import org.springframework.cloud.sleuth.zipkin.ZipkinProperties; +import org.springframework.cloud.sleuth.zipkin.ZipkinSpanReporter; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; +import zipkin.Span; import java.util.ArrayList; import java.util.List; @@ -18,23 +26,50 @@ @EnableZuulProxy @EnableEurekaClient public class GatewayApplication { - public static void main(String[] args) { - SpringApplication.run(GatewayApplication.class, args); - } - - @Autowired(required = false) - private List configurations = new ArrayList<>(); - - @Bean - @LoadBalanced - RestTemplate restTemplate() { - return new RestTemplate(); - } - - @Bean - public SpringClientFactory springClientFactory() { - SpringClientFactory factory = new SpringClientFactory(); - factory.setConfigurations(this.configurations); - return factory; - } + public static void main(String[] args) { + SpringApplication.run(GatewayApplication.class, args); + } + + @Autowired(required = false) + private List configurations = new ArrayList<>(); + @Autowired + private EurekaClient eurekaClient; + @Autowired + private SpanMetricReporter spanMetricReporter; + @Autowired + private ZipkinProperties zipkinProperties; + @Value("${spring.sleuth.web.skipPattern}") + private String skipPattern; + + @Bean + @LoadBalanced + RestTemplate restTemplate() { + return new RestTemplate(); + } + + @Bean + public SpringClientFactory springClientFactory() { + SpringClientFactory factory = new SpringClientFactory(); + factory.setConfigurations(this.configurations); + return factory; + } + + @Bean + public ZipkinSpanReporter makeZipkinSpanReporter() { + return new ZipkinSpanReporter() { + private HttpZipkinSpanReporter delegate; + private String baseUrl; + + @Override + public void report(Span span) { + InstanceInfo instance = eurekaClient.getNextServerFromEureka("zipkin", false); + if (!(baseUrl != null && instance.getHomePageUrl().equals(baseUrl))) { + baseUrl = instance.getHomePageUrl(); + delegate = new HttpZipkinSpanReporter(baseUrl, zipkinProperties.getFlushInterval(), zipkinProperties.getCompression().isEnabled(), spanMetricReporter); + if (!span.name.matches(skipPattern)) delegate.report(span); + } + if (!span.name.matches(skipPattern)) delegate.report(span); + } + }; + } } diff --git a/spring-cloud/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/SecurityConfig.java b/spring-cloud/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/SecurityConfig.java index 9e5c42440328..935e50ec720e 100644 --- a/spring-cloud/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/SecurityConfig.java +++ b/spring-cloud/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/SecurityConfig.java @@ -23,6 +23,7 @@ public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/book-service/books").permitAll() + .antMatchers("/zipkin/**").permitAll() .antMatchers("/eureka/**").hasRole("ADMIN") .anyRequest().authenticated() .and() diff --git a/spring-cloud/spring-cloud-bootstrap/gateway/src/test/java/com/baeldung/spring/cloud/bootstrap/gateway/LiveTest.java b/spring-cloud/spring-cloud-bootstrap/gateway/src/test/java/com/baeldung/spring/cloud/bootstrap/gateway/LiveTest.java new file mode 100644 index 000000000000..71ed2cd709f5 --- /dev/null +++ b/spring-cloud/spring-cloud-bootstrap/gateway/src/test/java/com/baeldung/spring/cloud/bootstrap/gateway/LiveTest.java @@ -0,0 +1,198 @@ +package com.baeldung.spring.cloud.bootstrap.gateway; + +import static io.restassured.RestAssured.config; +import io.restassured.RestAssured; +import io.restassured.authentication.FormAuthConfig; +import io.restassured.config.RedirectConfig; +import io.restassured.http.ContentType; +import io.restassured.response.Response; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.springframework.http.HttpStatus; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +public class LiveTest { + + private final String ROOT_URI = "http://localhost:8080"; + private final FormAuthConfig formConfig = new FormAuthConfig("/login", "username", "password"); + + @Before + public void setup() { + RestAssured.config = config().redirect(RedirectConfig.redirectConfig() + .followRedirects(false)); + } + + @Test + public void whenGetAllBooks_thenSuccess() { + final Response response = RestAssured.get(ROOT_URI + "/book-service/books"); + Assert.assertEquals(HttpStatus.OK.value(), response.getStatusCode()); + Assert.assertNotNull(response.getBody()); + } + + @Test + public void whenAccessProtectedResourceWithoutLogin_thenRedirectToLogin() { + final Response response = RestAssured.get(ROOT_URI + "/book-service/books/1"); + Assert.assertEquals(HttpStatus.FOUND.value(), response.getStatusCode()); + Assert.assertEquals("http://localhost:8080/login", response.getHeader("Location")); + } + + @Test + public void whenAccessProtectedResourceAfterLogin_thenSuccess() { + final Response response = RestAssured.given() + .auth() + .form("user", "password", formConfig) + .get(ROOT_URI + "/book-service/books/1"); + Assert.assertEquals(HttpStatus.OK.value(), response.getStatusCode()); + Assert.assertNotNull(response.getBody()); + } + + @Test + public void whenAccessAdminProtectedResource_thenForbidden() { + final Response response = RestAssured.given() + .auth() + .form("user", "password", formConfig) + .get(ROOT_URI + "/rating-service/ratings"); + Assert.assertEquals(HttpStatus.FORBIDDEN.value(), response.getStatusCode()); + + } + + @Test + public void whenAdminAccessProtectedResource_thenSuccess() { + final Response response = RestAssured.given() + .auth() + .form("admin", "admin", formConfig) + .get(ROOT_URI + "/rating-service/ratings"); + Assert.assertEquals(HttpStatus.OK.value(), response.getStatusCode()); + Assert.assertNotNull(response.getBody()); + } + + @Test + public void whenAdminAccessDiscoveryResource_thenSuccess() { + final Response response = RestAssured.given() + .auth() + .form("admin", "admin", formConfig) + .get(ROOT_URI + "/discovery"); + Assert.assertEquals(HttpStatus.OK.value(), response.getStatusCode()); + } + + @Test + public void whenAddnewRating_thenSuccess() { + + final Rating rating = new Rating(1L, 4); + + // request the protected resource + final Response ratingResponse = RestAssured.given() + .auth() + .form("admin", "admin", formConfig) + .and() + .contentType(ContentType.JSON) + .body(rating) + .post(ROOT_URI + "/rating-service/ratings"); + final Rating result = ratingResponse.as(Rating.class); + Assert.assertEquals(HttpStatus.OK.value(), ratingResponse.getStatusCode()); + Assert.assertEquals(rating.getBookId(), result.getBookId()); + Assert.assertEquals(rating.getStars(), result.getStars()); + } + + @Test + public void whenAddnewBook_thenSuccess() { + final Book book = new Book("Baeldung", "How to spring cloud"); + + // request the protected resource + final Response bookResponse = RestAssured.given() + .auth() + .form("admin", "admin", formConfig) + .and() + .contentType(ContentType.JSON) + .body(book) + .post(ROOT_URI + "/book-service/books"); + final Book result = bookResponse.as(Book.class); + Assert.assertEquals(HttpStatus.OK.value(), bookResponse.getStatusCode()); + Assert.assertEquals(book.getAuthor(), result.getAuthor()); + Assert.assertEquals(book.getTitle(), result.getTitle()); + + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Book { + + private Long id; + private String author; + private String title; + + public Book() { + } + + public Book(String author, String title) { + this.author = author; + this.title = title; + } + + public String getAuthor() { + return author; + } + + public void setAuthor(String author) { + this.author = author; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Rating { + private Long id; + private Long bookId; + private int stars; + + public Rating() { + } + + public Rating(Long bookId, int stars) { + this.bookId = bookId; + this.stars = stars; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getBookId() { + return bookId; + } + + public void setBookId(Long bookId) { + this.bookId = bookId; + } + + public int getStars() { + return stars; + } + + public void setStars(int stars) { + this.stars = stars; + } + } + +} \ No newline at end of file diff --git a/spring-cloud/spring-cloud-bootstrap/pom.xml b/spring-cloud/spring-cloud-bootstrap/pom.xml index ccfbdb97358f..83879cf7d4f2 100644 --- a/spring-cloud/spring-cloud-bootstrap/pom.xml +++ b/spring-cloud/spring-cloud-bootstrap/pom.xml @@ -16,6 +16,7 @@ gateway svc-book svc-rating + zipkin diff --git a/spring-cloud/spring-cloud-bootstrap/svc-book/pom.xml b/spring-cloud/spring-cloud-bootstrap/svc-book/pom.xml index c351c444f614..cbf7083b831d 100644 --- a/spring-cloud/spring-cloud-bootstrap/svc-book/pom.xml +++ b/spring-cloud/spring-cloud-bootstrap/svc-book/pom.xml @@ -53,6 +53,11 @@ runtime + + org.springframework.cloud + spring-cloud-starter-zipkin + + org.springframework.boot spring-boot-starter-test diff --git a/spring-cloud/spring-cloud-bootstrap/svc-book/src/main/java/com/baeldung/spring/cloud/bootstrap/svcbook/BookServiceApplication.java b/spring-cloud/spring-cloud-bootstrap/svc-book/src/main/java/com/baeldung/spring/cloud/bootstrap/svcbook/BookServiceApplication.java index c5499cd92406..3d55a59dbc6f 100644 --- a/spring-cloud/spring-cloud-bootstrap/svc-book/src/main/java/com/baeldung/spring/cloud/bootstrap/svcbook/BookServiceApplication.java +++ b/spring-cloud/spring-cloud-bootstrap/svc-book/src/main/java/com/baeldung/spring/cloud/bootstrap/svcbook/BookServiceApplication.java @@ -1,13 +1,51 @@ package com.baeldung.spring.cloud.bootstrap.svcbook; +import com.netflix.appinfo.InstanceInfo; +import com.netflix.discovery.EurekaClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; +import org.springframework.cloud.sleuth.metric.SpanMetricReporter; +import org.springframework.cloud.sleuth.zipkin.HttpZipkinSpanReporter; +import org.springframework.cloud.sleuth.zipkin.ZipkinProperties; +import org.springframework.cloud.sleuth.zipkin.ZipkinSpanReporter; +import org.springframework.context.annotation.Bean; +import zipkin.Span; @SpringBootApplication @EnableEurekaClient public class BookServiceApplication { + + @Autowired + private EurekaClient eurekaClient; + @Autowired + private SpanMetricReporter spanMetricReporter; + @Autowired + private ZipkinProperties zipkinProperties; + @Value("${spring.sleuth.web.skipPattern}") + private String skipPattern; + public static void main(String[] args) { SpringApplication.run(BookServiceApplication.class, args); } + + @Bean + public ZipkinSpanReporter makeZipkinSpanReporter() { + return new ZipkinSpanReporter() { + private HttpZipkinSpanReporter delegate; + private String baseUrl; + + @Override + public void report(Span span) { + InstanceInfo instance = eurekaClient.getNextServerFromEureka("zipkin", false); + if (!(baseUrl != null && instance.getHomePageUrl().equals(baseUrl))) { + baseUrl = instance.getHomePageUrl(); + delegate = new HttpZipkinSpanReporter(baseUrl, zipkinProperties.getFlushInterval(), zipkinProperties.getCompression().isEnabled(), spanMetricReporter); + if (!span.name.matches(skipPattern)) delegate.report(span); + } + } + }; + } } diff --git a/spring-cloud/spring-cloud-bootstrap/svc-book/src/main/java/com/baeldung/spring/cloud/bootstrap/svcbook/book/BookController.java b/spring-cloud/spring-cloud-bootstrap/svc-book/src/main/java/com/baeldung/spring/cloud/bootstrap/svcbook/book/BookController.java index d00f114b8c3a..192f9c2342b3 100644 --- a/spring-cloud/spring-cloud-bootstrap/svc-book/src/main/java/com/baeldung/spring/cloud/bootstrap/svcbook/book/BookController.java +++ b/spring-cloud/spring-cloud-bootstrap/svc-book/src/main/java/com/baeldung/spring/cloud/bootstrap/svcbook/book/BookController.java @@ -1,11 +1,19 @@ package com.baeldung.spring.cloud.bootstrap.svcbook.book; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.*; - import java.util.List; import java.util.Map; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + @RestController @RequestMapping("/books") public class BookController { @@ -13,7 +21,7 @@ public class BookController { @Autowired private BookService bookService; - @GetMapping("") + @GetMapping public List findAllBooks() { return bookService.findAllBooks(); } @@ -23,7 +31,7 @@ public Book findBook(@PathVariable Long bookId) { return bookService.findBookById(bookId); } - @PostMapping("") + @PostMapping public Book createBook(@RequestBody Book book) { return bookService.createBook(book); } @@ -33,7 +41,12 @@ public void deleteBook(@PathVariable Long bookId) { bookService.deleteBook(bookId); } - @PatchMapping("/{bookId") + @PutMapping("/{bookId}") + public Book updateBook(@RequestBody Book book, @PathVariable Long bookId) { + return bookService.updateBook(book, bookId); + } + + @PatchMapping("/{bookId}") public Book updateBook(@RequestBody Map updates, @PathVariable Long bookId) { return bookService.updateBook(updates, bookId); } diff --git a/spring-cloud/spring-cloud-bootstrap/svc-book/src/main/java/com/baeldung/spring/cloud/bootstrap/svcbook/book/BookService.java b/spring-cloud/spring-cloud-bootstrap/svc-book/src/main/java/com/baeldung/spring/cloud/bootstrap/svcbook/book/BookService.java index cfcbf15757d8..106fdad5d97c 100644 --- a/spring-cloud/spring-cloud-bootstrap/svc-book/src/main/java/com/baeldung/spring/cloud/bootstrap/svcbook/book/BookService.java +++ b/spring-cloud/spring-cloud-bootstrap/svc-book/src/main/java/com/baeldung/spring/cloud/bootstrap/svcbook/book/BookService.java @@ -1,13 +1,15 @@ package com.baeldung.spring.cloud.bootstrap.svcbook.book; +import java.util.List; +import java.util.Map; +import java.util.Optional; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import com.google.common.base.Preconditions; @Service @Transactional(readOnly = true) @@ -27,7 +29,7 @@ public Book findBookById(Long bookId) { @Transactional(propagation = Propagation.REQUIRED) public Book createBook(Book book) { - Book newBook = new Book(); + final Book newBook = new Book(); newBook.setTitle(book.getTitle()); newBook.setAuthor(book.getAuthor()); return bookRepository.save(newBook); @@ -40,16 +42,25 @@ public void deleteBook(Long bookId) { @Transactional(propagation = Propagation.REQUIRED) public Book updateBook(Map updates, Long bookId) { - Book book = findBookById(bookId); - updates.keySet().forEach(key -> { - switch (key) { - case "author": - book.setAuthor(updates.get(key)); - break; - case "title": - book.setTitle(updates.get(key)); - } - }); + final Book book = findBookById(bookId); + updates.keySet() + .forEach(key -> { + switch (key) { + case "author": + book.setAuthor(updates.get(key)); + break; + case "title": + book.setTitle(updates.get(key)); + } + }); + return bookRepository.save(book); + } + + @Transactional(propagation = Propagation.REQUIRED) + public Book updateBook(Book book, Long bookId) { + Preconditions.checkNotNull(book); + Preconditions.checkState(book.getId() == bookId); + Preconditions.checkNotNull(bookRepository.findOne(bookId)); return bookRepository.save(book); } } diff --git a/spring-cloud/spring-cloud-bootstrap/svc-book/src/main/resources/bootstrap.properties b/spring-cloud/spring-cloud-bootstrap/svc-book/src/main/resources/bootstrap.properties index 8f3a3261acbb..481cdc182cca 100644 --- a/spring-cloud/spring-cloud-bootstrap/svc-book/src/main/resources/bootstrap.properties +++ b/spring-cloud/spring-cloud-bootstrap/svc-book/src/main/resources/bootstrap.properties @@ -4,4 +4,4 @@ spring.cloud.config.discovery.enabled=true spring.cloud.config.username=configUser spring.cloud.config.password=configPassword -eureka.client.serviceUrl.defaultZone=http://discUser:discPassword@localhost:8082/eureka/ +eureka.client.serviceUrl.defaultZone=http://discUser:discPassword@localhost:8082/eureka/ \ No newline at end of file diff --git a/spring-cloud/spring-cloud-bootstrap/svc-rating/pom.xml b/spring-cloud/spring-cloud-bootstrap/svc-rating/pom.xml index 2285286812ae..f02ba88f04d8 100644 --- a/spring-cloud/spring-cloud-bootstrap/svc-rating/pom.xml +++ b/spring-cloud/spring-cloud-bootstrap/svc-rating/pom.xml @@ -53,6 +53,11 @@ runtime + + org.springframework.cloud + spring-cloud-starter-zipkin + + org.springframework.boot spring-boot-starter-test diff --git a/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/RatingServiceApplication.java b/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/RatingServiceApplication.java index 61074e0bccc8..0d85c5c8257d 100644 --- a/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/RatingServiceApplication.java +++ b/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/RatingServiceApplication.java @@ -1,13 +1,51 @@ package com.baeldung.spring.cloud.bootstrap.svcrating; +import com.netflix.appinfo.InstanceInfo; +import com.netflix.discovery.EurekaClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; +import org.springframework.cloud.sleuth.metric.SpanMetricReporter; +import org.springframework.cloud.sleuth.zipkin.HttpZipkinSpanReporter; +import org.springframework.cloud.sleuth.zipkin.ZipkinProperties; +import org.springframework.cloud.sleuth.zipkin.ZipkinSpanReporter; +import org.springframework.context.annotation.Bean; +import zipkin.Span; @SpringBootApplication @EnableEurekaClient public class RatingServiceApplication { + @Autowired + private EurekaClient eurekaClient; + @Autowired + private SpanMetricReporter spanMetricReporter; + @Autowired + private ZipkinProperties zipkinProperties; + @Value("${spring.sleuth.web.skipPattern}") + private String skipPattern; + public static void main(String[] args) { SpringApplication.run(RatingServiceApplication.class, args); } + + @Bean + public ZipkinSpanReporter makeZipkinSpanReporter() { + return new ZipkinSpanReporter() { + private HttpZipkinSpanReporter delegate; + private String baseUrl; + + @Override + public void report(Span span) { + InstanceInfo instance = eurekaClient.getNextServerFromEureka("zipkin", false); + if (!(baseUrl != null && instance.getHomePageUrl().equals(baseUrl))) { + baseUrl = instance.getHomePageUrl(); + delegate = new HttpZipkinSpanReporter(baseUrl, zipkinProperties.getFlushInterval(), zipkinProperties.getCompression().isEnabled(), spanMetricReporter); + if (!span.name.matches(skipPattern)) delegate.report(span); + } + if (!span.name.matches(skipPattern)) delegate.report(span); + } + }; + } } \ No newline at end of file diff --git a/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/RatingController.java b/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/RatingController.java index 83452ad74758..91966034f6eb 100644 --- a/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/RatingController.java +++ b/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/RatingController.java @@ -1,10 +1,20 @@ package com.baeldung.spring.cloud.bootstrap.svcrating.rating; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.*; - import java.util.List; import java.util.Map; +import java.util.Optional; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/ratings") @@ -13,15 +23,13 @@ public class RatingController { @Autowired private RatingService ratingService; - @GetMapping("") - public List findRatingsByBookId(@RequestParam(required = false, defaultValue = "0") Long bookId) { - if (bookId.equals(0L)) { - return ratingService.findAllRatings(); - } - return ratingService.findRatingsByBookId(bookId); + @GetMapping + public List findRatingsByBookId(@RequestParam(required = false) Optional bookId) { + return bookId.map(ratingService::findRatingsByBookId) + .orElseGet(ratingService::findAllRatings); } - @PostMapping("") + @PostMapping public Rating createRating(@RequestBody Rating rating) { return ratingService.createRating(rating); } @@ -31,7 +39,12 @@ public void deleteRating(@PathVariable Long ratingId) { ratingService.deleteRating(ratingId); } - @PatchMapping("/{ratingId") + @PutMapping("/{ratingId}") + public Rating updateRating(@RequestBody Rating rating, @PathVariable Long ratingId) { + return ratingService.updateRating(rating, ratingId); + } + + @PatchMapping("/{ratingId}") public Rating updateRating(@RequestBody Map updates, @PathVariable Long ratingId) { return ratingService.updateRating(updates, ratingId); } diff --git a/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/RatingService.java b/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/RatingService.java index a2360b7be5ad..b05d7e2f1f7b 100644 --- a/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/RatingService.java +++ b/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/RatingService.java @@ -1,13 +1,15 @@ package com.baeldung.spring.cloud.bootstrap.svcrating.rating; +import java.util.List; +import java.util.Map; +import java.util.Optional; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import com.google.common.base.Preconditions; @Service @Transactional(readOnly = true) @@ -31,7 +33,7 @@ public List findAllRatings() { @Transactional(propagation = Propagation.REQUIRED) public Rating createRating(Rating rating) { - Rating newRating = new Rating(); + final Rating newRating = new Rating(); newRating.setBookId(rating.getBookId()); newRating.setStars(rating.getStars()); return ratingRepository.save(newRating); @@ -44,14 +46,22 @@ public void deleteRating(Long ratingId) { @Transactional(propagation = Propagation.REQUIRED) public Rating updateRating(Map updates, Long ratingId) { - Rating rating = findRatingById(ratingId); - updates.keySet().forEach(key -> { - switch (key) { + final Rating rating = findRatingById(ratingId); + updates.keySet() + .forEach(key -> { + switch (key) { case "stars": rating.setStars(Integer.parseInt(updates.get(key))); break; - } - }); + } + }); + return ratingRepository.save(rating); + } + + public Rating updateRating(Rating rating, Long ratingId) { + Preconditions.checkNotNull(rating); + Preconditions.checkState(rating.getId() == ratingId); + Preconditions.checkNotNull(ratingRepository.findOne(ratingId)); return ratingRepository.save(rating); } } diff --git a/spring-cloud/spring-cloud-bootstrap/zipkin/pom.xml b/spring-cloud/spring-cloud-bootstrap/zipkin/pom.xml new file mode 100644 index 000000000000..c2fd7cd2dcc5 --- /dev/null +++ b/spring-cloud/spring-cloud-bootstrap/zipkin/pom.xml @@ -0,0 +1,88 @@ + + + 4.0.0 + + zipkin + 1.0.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 1.4.4.RELEASE + + + + + + org.springframework.cloud + spring-cloud-starter-config + + + org.springframework.cloud + spring-cloud-starter-eureka + + + + io.zipkin.java + zipkin-server + + + + io.zipkin.java + zipkin-autoconfigure-ui + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud-dependencies.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*LiveTest.java + + + + + + + + Brixton.SR7 + 3.6.0 + + \ No newline at end of file diff --git a/spring-cloud/spring-cloud-bootstrap/zipkin/src/main/java/com/baeldung/spring/cloud/bootstrap/zipkin/ZipkinApplication.java b/spring-cloud/spring-cloud-bootstrap/zipkin/src/main/java/com/baeldung/spring/cloud/bootstrap/zipkin/ZipkinApplication.java new file mode 100644 index 000000000000..d757567156b4 --- /dev/null +++ b/spring-cloud/spring-cloud-bootstrap/zipkin/src/main/java/com/baeldung/spring/cloud/bootstrap/zipkin/ZipkinApplication.java @@ -0,0 +1,15 @@ +package com.baeldung.spring.cloud.bootstrap.zipkin; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.eureka.EnableEurekaClient; +import zipkin.server.EnableZipkinServer; + +@SpringBootApplication +@EnableEurekaClient +@EnableZipkinServer +public class ZipkinApplication { + public static void main(String[] args) { + SpringApplication.run(ZipkinApplication.class, args); + } +} diff --git a/spring-cloud/spring-cloud-bootstrap/zipkin/src/main/resources/bootstrap.properties b/spring-cloud/spring-cloud-bootstrap/zipkin/src/main/resources/bootstrap.properties new file mode 100644 index 000000000000..9569179a4fb2 --- /dev/null +++ b/spring-cloud/spring-cloud-bootstrap/zipkin/src/main/resources/bootstrap.properties @@ -0,0 +1,7 @@ +spring.cloud.config.name=zipkin +spring.cloud.config.discovery.service-id=config +spring.cloud.config.discovery.enabled=true +spring.cloud.config.username=configUser +spring.cloud.config.password=configPassword + +eureka.client.serviceUrl.defaultZone=http://discUser:discPassword@localhost:8082/eureka/ \ No newline at end of file diff --git a/spring-cloud/spring-cloud-rest/spring-cloud-rest-discovery-server/pom.xml b/spring-cloud/spring-cloud-rest/spring-cloud-rest-discovery-server/pom.xml index d9d08079b7ef..d9ef26280b4b 100644 --- a/spring-cloud/spring-cloud-rest/spring-cloud-rest-discovery-server/pom.xml +++ b/spring-cloud/spring-cloud-rest/spring-cloud-rest-discovery-server/pom.xml @@ -76,6 +76,8 @@ org.apache.maven.plugins maven-surefire-plugin + 3 + true **/*IntegrationTest.java **/*LiveTest.java diff --git a/spring-cloud/spring-cloud-zookeeper/Greeting/pom.xml b/spring-cloud/spring-cloud-zookeeper/Greeting/pom.xml new file mode 100644 index 000000000000..78a0e05b7782 --- /dev/null +++ b/spring-cloud/spring-cloud-zookeeper/Greeting/pom.xml @@ -0,0 +1,79 @@ + + + 4.0.0 + + com.baeldung.spring.cloud + spring-cloud-zookeeper + 1.0.0-SNAPSHOT + + Greeting + jar + + + org.springframework.boot + spring-boot-starter + 1.5.2.RELEASE + + + org.springframework + spring-web + 4.3.7.RELEASE + + + org.springframework.cloud + spring-cloud-starter-zookeeper-discovery + 1.0.3.RELEASE + + + commons-logging + commons-logging + + + + + org.springframework.boot + spring-boot-starter-actuator + 1.5.2.RELEASE + + + org.springframework.cloud + spring-cloud-starter-feign + 1.2.5.RELEASE + + + junit + junit + 4.12 + test + + + org.hamcrest + hamcrest-core + 1.3 + test + + + + + + + org.springframework.cloud + spring-cloud-dependencies + Brixton.SR7 + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + Greeting + \ No newline at end of file diff --git a/spring-cloud/spring-cloud-zookeeper/Greeting/src/main/java/com/baeldung/spring/cloud/greeting/GreetingApplication.java b/spring-cloud/spring-cloud-zookeeper/Greeting/src/main/java/com/baeldung/spring/cloud/greeting/GreetingApplication.java new file mode 100644 index 000000000000..8a1662999bb2 --- /dev/null +++ b/spring-cloud/spring-cloud-zookeeper/Greeting/src/main/java/com/baeldung/spring/cloud/greeting/GreetingApplication.java @@ -0,0 +1,20 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.baeldung.spring.cloud.greeting; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; + +@SpringBootApplication +@EnableDiscoveryClient +public class GreetingApplication { + + public static void main(String[] args) { + SpringApplication.run(GreetingApplication.class, args); + } + +} diff --git a/spring-cloud/spring-cloud-zookeeper/Greeting/src/main/java/com/baeldung/spring/cloud/greeting/GreetingController.java b/spring-cloud/spring-cloud-zookeeper/Greeting/src/main/java/com/baeldung/spring/cloud/greeting/GreetingController.java new file mode 100644 index 000000000000..84792deed1fb --- /dev/null +++ b/spring-cloud/spring-cloud-zookeeper/Greeting/src/main/java/com/baeldung/spring/cloud/greeting/GreetingController.java @@ -0,0 +1,25 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.baeldung.spring.cloud.greeting; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class GreetingController { + + @Autowired + private HelloWorldClient helloWorldClient; + + @GetMapping("/get-greeting") + public String greeting() { + + return helloWorldClient.HelloWorld(); + + } + +} diff --git a/spring-cloud/spring-cloud-zookeeper/Greeting/src/main/java/com/baeldung/spring/cloud/greeting/HelloWorldClient.java b/spring-cloud/spring-cloud-zookeeper/Greeting/src/main/java/com/baeldung/spring/cloud/greeting/HelloWorldClient.java new file mode 100644 index 000000000000..c56cb1907ab1 --- /dev/null +++ b/spring-cloud/spring-cloud-zookeeper/Greeting/src/main/java/com/baeldung/spring/cloud/greeting/HelloWorldClient.java @@ -0,0 +1,41 @@ +package com.baeldung.spring.cloud.greeting; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.netflix.feign.EnableFeignClients; +import org.springframework.cloud.netflix.feign.FeignClient; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +/** + * This class provides operations on the Validation service. + * + *

+ * When booting up, Spring will try and find a service named "Validation" (see + * the FeignClient below) under the available ZooKeeper instance. + *

+ * + */ +@Configuration +@EnableFeignClients +@EnableDiscoveryClient +public class HelloWorldClient { + + @Autowired + private TheClient theClient; + + @FeignClient(name = "HelloWorld") + interface TheClient { + + @RequestMapping(path = "/helloworld", method = RequestMethod.GET) + @ResponseBody + String HelloWorld(); + } + + public String HelloWorld() { + return theClient.HelloWorld(); + } + +} diff --git a/spring-cloud/spring-cloud-zookeeper/Greeting/src/main/resources/application.yml b/spring-cloud/spring-cloud-zookeeper/Greeting/src/main/resources/application.yml new file mode 100644 index 000000000000..6140f6ab2fd1 --- /dev/null +++ b/spring-cloud/spring-cloud-zookeeper/Greeting/src/main/resources/application.yml @@ -0,0 +1,11 @@ +spring: + application: + name: Greeting + cloud: + zookeeper: + connect-string: localhost:2181 +server: + port: 8083 +logging: + level: + org.apache.zookeeper.ClientCnxn: WARN \ No newline at end of file diff --git a/spring-cloud/spring-cloud-zookeeper/Greeting/src/main/resources/templates/greeting-view.html b/spring-cloud/spring-cloud-zookeeper/Greeting/src/main/resources/templates/greeting-view.html new file mode 100644 index 000000000000..42cdadb487f2 --- /dev/null +++ b/spring-cloud/spring-cloud-zookeeper/Greeting/src/main/resources/templates/greeting-view.html @@ -0,0 +1,9 @@ + + + + Greeting Page + + +

+ + \ No newline at end of file diff --git a/spring-cloud/spring-cloud-zookeeper/HelloWorld/pom.xml b/spring-cloud/spring-cloud-zookeeper/HelloWorld/pom.xml new file mode 100644 index 000000000000..00af49eaa827 --- /dev/null +++ b/spring-cloud/spring-cloud-zookeeper/HelloWorld/pom.xml @@ -0,0 +1,57 @@ + + + 4.0.0 + + com.baeldung.spring.cloud + spring-cloud-zookeeper + 1.0.0-SNAPSHOT + + HelloWorld + jar + + + org.springframework.boot + spring-boot-starter + 1.5.2.RELEASE + + + org.springframework + spring-web + 4.3.7.RELEASE + + + org.springframework.cloud + spring-cloud-starter-zookeeper-discovery + 1.0.3.RELEASE + + + commons-logging + commons-logging + + + + + + + + + org.springframework.cloud + spring-cloud-dependencies + Brixton.SR7 + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + HelloWorld + \ No newline at end of file diff --git a/spring-cloud/spring-cloud-zookeeper/HelloWorld/src/main/java/com/baeldung/spring/cloud/helloworld/HelloWorldApplication.java b/spring-cloud/spring-cloud-zookeeper/HelloWorld/src/main/java/com/baeldung/spring/cloud/helloworld/HelloWorldApplication.java new file mode 100644 index 000000000000..8b3507151636 --- /dev/null +++ b/spring-cloud/spring-cloud-zookeeper/HelloWorld/src/main/java/com/baeldung/spring/cloud/helloworld/HelloWorldApplication.java @@ -0,0 +1,18 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.baeldung.spring.cloud.helloworld; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; + +@SpringBootApplication +@EnableDiscoveryClient +public class HelloWorldApplication { + public static void main(String[] args) { + SpringApplication.run(HelloWorldApplication.class, args); + } +} diff --git a/spring-cloud/spring-cloud-zookeeper/HelloWorld/src/main/java/com/baeldung/spring/cloud/helloworld/HelloWorldController.java b/spring-cloud/spring-cloud-zookeeper/HelloWorld/src/main/java/com/baeldung/spring/cloud/helloworld/HelloWorldController.java new file mode 100644 index 000000000000..6c662e11119a --- /dev/null +++ b/spring-cloud/spring-cloud-zookeeper/HelloWorld/src/main/java/com/baeldung/spring/cloud/helloworld/HelloWorldController.java @@ -0,0 +1,19 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.baeldung.spring.cloud.helloworld; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class HelloWorldController { + + @GetMapping("/helloworld") + public String HelloWorld() { + return "Hello World!"; + } + +} diff --git a/spring-cloud/spring-cloud-zookeeper/HelloWorld/src/main/resources/application.yml b/spring-cloud/spring-cloud-zookeeper/HelloWorld/src/main/resources/application.yml new file mode 100644 index 000000000000..550165a76222 --- /dev/null +++ b/spring-cloud/spring-cloud-zookeeper/HelloWorld/src/main/resources/application.yml @@ -0,0 +1,16 @@ +spring: + application: + name: HelloWorld + cloud: + zookeeper: + connect-string: localhost:2181 + discovery: + enabled: true +server: + port: 8081 +endpoints: + restart: + enabled: true +logging: + level: + org.apache.zookeeper.ClientCnxn: WARN \ No newline at end of file diff --git a/spring-cloud/spring-cloud-zookeeper/pom.xml b/spring-cloud/spring-cloud-zookeeper/pom.xml new file mode 100644 index 000000000000..840681316149 --- /dev/null +++ b/spring-cloud/spring-cloud-zookeeper/pom.xml @@ -0,0 +1,15 @@ + + + 4.0.0 + + com.baeldung.spring.cloud + spring-cloud + 1.0.0-SNAPSHOT + + spring-cloud-zookeeper + pom + + Greeting + HelloWorld + + \ No newline at end of file diff --git a/spring-core/README.md b/spring-core/README.md index a32d30939f80..3f2abe42a1d9 100644 --- a/spring-core/README.md +++ b/spring-core/README.md @@ -4,3 +4,4 @@ - [How to use the Spring FactoryBean?](http://www.baeldung.com/spring-factorybean) - [Constructor Dependency Injection in Spring](http://www.baeldung.com/constructor-injection-in-spring) - [Constructor Injection in Spring with Lombok](http://www.baeldung.com/spring-injection-lombok) +- [A Quick Guide to Spring @Value](http://www.baeldung.com/spring-value-annotation) diff --git a/spring-core/pom.xml b/spring-core/pom.xml index 3b423104d73e..175a9b64d177 100644 --- a/spring-core/pom.xml +++ b/spring-core/pom.xml @@ -24,7 +24,7 @@ org.springframework spring-core - ${spring.version} + ${spring.version} org.springframework diff --git a/spring-core/src/main/java/com/baeldung/beaninjection/AnotherSampleDAOBean.java b/spring-core/src/main/java/com/baeldung/beaninjection/AnotherSampleDAOBean.java new file mode 100644 index 000000000000..6313ba5a6590 --- /dev/null +++ b/spring-core/src/main/java/com/baeldung/beaninjection/AnotherSampleDAOBean.java @@ -0,0 +1,17 @@ +package com.baeldung.beaninjection; + +public class AnotherSampleDAOBean implements IAnotherSampleDAO { + + private String propertyY; + + public AnotherSampleDAOBean(String propertyY) { + this.propertyY = propertyY; + } + + // standard setters and getters + + public String getPropertyY() { + return propertyY; + } + +} diff --git a/spring-core/src/main/java/com/baeldung/beaninjection/ExampleDAOBean.java b/spring-core/src/main/java/com/baeldung/beaninjection/ExampleDAOBean.java new file mode 100644 index 000000000000..cbc7b35a7cb8 --- /dev/null +++ b/spring-core/src/main/java/com/baeldung/beaninjection/ExampleDAOBean.java @@ -0,0 +1,39 @@ +package com.baeldung.beaninjection; + +import java.util.List; + +public class ExampleDAOBean implements IExampleDAO { + + private String propertyX; + + public ExampleDAOBean(String propertyX) { + this.propertyX = propertyX; + } + + public String getPropertyX() { + return propertyX; + } + + public void setPropertyX(String propertyX) { + this.propertyX = propertyX; + } + + @Override + public List getDomainList() { + // TODO Auto-generated method stub + return null; + } + + @Override + public SampleDomainObject createNewDomain(SampleDomainObject inputDomain) { + // TODO Auto-generated method stub + return null; + } + + @Override + public SampleDomainObject getSomeDomain() { + // TODO Auto-generated method stub + return new SampleDomainObject(); + } + +} diff --git a/spring-core/src/main/java/com/baeldung/beaninjection/ExampleServiceBean.java b/spring-core/src/main/java/com/baeldung/beaninjection/ExampleServiceBean.java new file mode 100644 index 000000000000..e5a5dfff5dc5 --- /dev/null +++ b/spring-core/src/main/java/com/baeldung/beaninjection/ExampleServiceBean.java @@ -0,0 +1,47 @@ +package com.baeldung.beaninjection; + +import java.util.List; + +public class ExampleServiceBean implements IExampleService { + + private IExampleDAO exampleDAO; + private IAnotherSampleDAO anotherSampleDAO; + + public ExampleServiceBean(IExampleDAO exampleDAO) { + this.exampleDAO = exampleDAO; + } + + public void setAnotherSampleDAO(IAnotherSampleDAO anotherSampleDAO) { + this.anotherSampleDAO = anotherSampleDAO; + } + + // standard setters and getters + + public IAnotherSampleDAO getAnotherSampleDAO() { + return anotherSampleDAO; + } + + public void setExampleDAO(ExampleDAOBean exampleDAO) { + this.exampleDAO = exampleDAO; + } + + public IExampleDAO getExampleDAO() { + return exampleDAO; + } + + private String propertyX; + + public String getPropertyX() { + return propertyX; + } + + public void setPropertyX(String propertyX) { + this.propertyX = propertyX; + } + + public List serviceMethodX() { + /*get domain list from DAO .. business logic on domain objects..return*/ + return exampleDAO.getDomainList(); + } + +} diff --git a/spring-core/src/main/java/com/baeldung/beaninjection/IAnotherSampleDAO.java b/spring-core/src/main/java/com/baeldung/beaninjection/IAnotherSampleDAO.java new file mode 100644 index 000000000000..ed4ad427054e --- /dev/null +++ b/spring-core/src/main/java/com/baeldung/beaninjection/IAnotherSampleDAO.java @@ -0,0 +1,5 @@ +package com.baeldung.beaninjection; + +public interface IAnotherSampleDAO { + +} diff --git a/spring-core/src/main/java/com/baeldung/beaninjection/IExampleDAO.java b/spring-core/src/main/java/com/baeldung/beaninjection/IExampleDAO.java new file mode 100644 index 000000000000..f7dbd2f9fe2e --- /dev/null +++ b/spring-core/src/main/java/com/baeldung/beaninjection/IExampleDAO.java @@ -0,0 +1,12 @@ +package com.baeldung.beaninjection; + +import java.util.List; + +public interface IExampleDAO { + + List getDomainList(); + + SampleDomainObject createNewDomain(SampleDomainObject domainObject); + + SampleDomainObject getSomeDomain(); +} diff --git a/spring-core/src/main/java/com/baeldung/beaninjection/IExampleService.java b/spring-core/src/main/java/com/baeldung/beaninjection/IExampleService.java new file mode 100644 index 000000000000..9ea572d16bf3 --- /dev/null +++ b/spring-core/src/main/java/com/baeldung/beaninjection/IExampleService.java @@ -0,0 +1,9 @@ +package com.baeldung.beaninjection; + +import java.util.List; + +public interface IExampleService { + + List serviceMethodX(); + +} diff --git a/spring-core/src/main/java/com/baeldung/beaninjection/SampleDomainObject.java b/spring-core/src/main/java/com/baeldung/beaninjection/SampleDomainObject.java new file mode 100644 index 000000000000..3a5a913aa632 --- /dev/null +++ b/spring-core/src/main/java/com/baeldung/beaninjection/SampleDomainObject.java @@ -0,0 +1,31 @@ +package com.baeldung.beaninjection; + +import java.io.Serializable; + +public class SampleDomainObject implements Serializable { + + /** + * + */ + private static final long serialVersionUID = 449859763481296747L; + + private String domainPropX; + private String domainPropY; + + public String getDomainPropX() { + return domainPropX; + } + + public void setDomainPropX(String domainPropX) { + this.domainPropX = domainPropX; + } + + public String getDomainPropY() { + return domainPropY; + } + + public void setDomainPropY(String domainPropY) { + this.domainPropY = domainPropY; + } + +} diff --git a/spring-core/src/main/java/com/baeldung/configuration/ApplicationContextTestBeanInjectionTypes.java b/spring-core/src/main/java/com/baeldung/configuration/ApplicationContextTestBeanInjectionTypes.java new file mode 100644 index 000000000000..3939abf1481e --- /dev/null +++ b/spring-core/src/main/java/com/baeldung/configuration/ApplicationContextTestBeanInjectionTypes.java @@ -0,0 +1,36 @@ +package com.baeldung.configuration; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +import com.baeldung.beaninjection.AnotherSampleDAOBean; +import com.baeldung.beaninjection.ExampleDAOBean; +import com.baeldung.beaninjection.ExampleServiceBean; +import com.baeldung.beaninjection.IAnotherSampleDAO; +import com.baeldung.beaninjection.IExampleDAO; +import com.baeldung.beaninjection.IExampleService; + +@Configuration +@ComponentScan(basePackages = { "com.baeldung.beaninjection" }) +public class ApplicationContextTestBeanInjectionTypes { + + @Bean + public IExampleDAO exampleDAO() { + return new ExampleDAOBean("Mandatory DAO Property X"); + } + + @Bean + public IExampleService exampleServiceBean() { + ExampleServiceBean serviceBean = new ExampleServiceBean(exampleDAO()); + serviceBean.setAnotherSampleDAO(anotherSampleDAO()); + serviceBean.setPropertyX("Some Service Property X"); + return serviceBean; + } + + @Bean + public IAnotherSampleDAO anotherSampleDAO() { + return new AnotherSampleDAOBean("Mandatory DAO Property Y"); + } + +} diff --git a/spring-core/src/main/java/com/baeldung/constructordi/SpringRunner.java b/spring-core/src/main/java/com/baeldung/constructordi/SpringRunner.java index 623739f036e4..bffd35ec5935 100644 --- a/spring-core/src/main/java/com/baeldung/constructordi/SpringRunner.java +++ b/spring-core/src/main/java/com/baeldung/constructordi/SpringRunner.java @@ -1,31 +1,31 @@ -package com.baeldung.constructordi; - -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; - -import com.baeldung.constructordi.domain.Car; - -public class SpringRunner { - public static void main(String[] args) { - Car toyota = getCarFromXml(); - - System.out.println(toyota); - - toyota = getCarFromJavaConfig(); - - System.out.println(toyota); - } - - private static Car getCarFromJavaConfig() { - ApplicationContext context = new AnnotationConfigApplicationContext(Config.class); - - return context.getBean(Car.class); - } - - private static Car getCarFromXml() { - ApplicationContext context = new ClassPathXmlApplicationContext("baeldung.xml"); - - return context.getBean(Car.class); - } -} +package com.baeldung.constructordi; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +import com.baeldung.constructordi.domain.Car; + +public class SpringRunner { + public static void main(String[] args) { + Car toyota = getCarFromXml(); + + System.out.println(toyota); + + toyota = getCarFromJavaConfig(); + + System.out.println(toyota); + } + + private static Car getCarFromJavaConfig() { + ApplicationContext context = new AnnotationConfigApplicationContext(Config.class); + + return context.getBean(Car.class); + } + + private static Car getCarFromXml() { + ApplicationContext context = new ClassPathXmlApplicationContext("constructordi.xml"); + + return context.getBean(Car.class); + } +} diff --git a/spring-core/src/main/java/com/baeldung/constructordi/domain/Car.java b/spring-core/src/main/java/com/baeldung/constructordi/domain/Car.java index 9f68ba5cd94d..5c9467fdf4ac 100644 --- a/spring-core/src/main/java/com/baeldung/constructordi/domain/Car.java +++ b/spring-core/src/main/java/com/baeldung/constructordi/domain/Car.java @@ -1,21 +1,21 @@ -package com.baeldung.constructordi.domain; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -@Component -public class Car { - private Engine engine; - private Transmission transmission; - - @Autowired - public Car(Engine engine, Transmission transmission) { - this.engine = engine; - this.transmission = transmission; - } - - @Override - public String toString() { - return String.format("Engine: %s Transmission: %s", engine, transmission); - } -} +package com.baeldung.constructordi.domain; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class Car { + private Engine engine; + private Transmission transmission; + + @Autowired + public Car(Engine engine, Transmission transmission) { + this.engine = engine; + this.transmission = transmission; + } + + @Override + public String toString() { + return String.format("Engine: %s Transmission: %s", engine, transmission); + } +} diff --git a/spring-core/src/main/java/com/baeldung/lombok/ApologizeService.java b/spring-core/src/main/java/com/baeldung/lombok/ApologizeService.java index 25ef65cad2c5..76c3df821710 100644 --- a/spring-core/src/main/java/com/baeldung/lombok/ApologizeService.java +++ b/spring-core/src/main/java/com/baeldung/lombok/ApologizeService.java @@ -1,11 +1,11 @@ package com.baeldung.lombok; -import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component -@AllArgsConstructor +@RequiredArgsConstructor public class ApologizeService { private final Translator translator; diff --git a/spring-core/src/main/java/com/baeldung/lombok/ThankingService.java b/spring-core/src/main/java/com/baeldung/lombok/ThankingService.java index f3bdf8bb7f14..2e0c398d2de9 100644 --- a/spring-core/src/main/java/com/baeldung/lombok/ThankingService.java +++ b/spring-core/src/main/java/com/baeldung/lombok/ThankingService.java @@ -1,10 +1,10 @@ package com.baeldung.lombok; -import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; @Component -@AllArgsConstructor +@RequiredArgsConstructor public class ThankingService { private final Translator translator; diff --git a/spring-core/src/main/java/com/baeldung/setterdi/Config.java b/spring-core/src/main/java/com/baeldung/setterdi/Config.java new file mode 100644 index 000000000000..68c1ae12a2ae --- /dev/null +++ b/spring-core/src/main/java/com/baeldung/setterdi/Config.java @@ -0,0 +1,35 @@ +package com.baeldung.setterdi; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +import com.baeldung.setterdi.domain.Engine; +import com.baeldung.setterdi.domain.Trailer; +import com.baeldung.setterdi.domain.Transmission; + +@Configuration +@ComponentScan("com.baeldung.setterdi") +public class Config { + + @Bean + public Engine engine() { + Engine engine = new Engine(); + engine.setType("v8"); + engine.setVolume(5); + return engine; + } + + @Bean + public Transmission transmission() { + Transmission transmission = new Transmission(); + transmission.setType("sliding"); + return transmission; + } + + @Bean + public Trailer trailer() { + Trailer trailer = new Trailer(); + return trailer; + } +} \ No newline at end of file diff --git a/spring-core/src/main/java/com/baeldung/setterdi/SpringRunner.java b/spring-core/src/main/java/com/baeldung/setterdi/SpringRunner.java new file mode 100644 index 000000000000..bc92b79e8ab7 --- /dev/null +++ b/spring-core/src/main/java/com/baeldung/setterdi/SpringRunner.java @@ -0,0 +1,33 @@ +package com.baeldung.setterdi; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +import com.baeldung.setterdi.Config; +import com.baeldung.setterdi.domain.Car; + +public class SpringRunner { + public static void main(String[] args) { + Car toyota = getCarFromXml(); + + System.out.println(toyota); + + toyota = getCarFromJavaConfig(); + + System.out.println(toyota); + + } + + private static Car getCarFromJavaConfig() { + ApplicationContext context = new AnnotationConfigApplicationContext(Config.class); + + return context.getBean(Car.class); + } + + private static Car getCarFromXml() { + ApplicationContext context = new ClassPathXmlApplicationContext("setterdi.xml"); + + return context.getBean(Car.class); + } +} \ No newline at end of file diff --git a/spring-core/src/main/java/com/baeldung/setterdi/domain/Car.java b/spring-core/src/main/java/com/baeldung/setterdi/domain/Car.java new file mode 100644 index 000000000000..749ff3984a79 --- /dev/null +++ b/spring-core/src/main/java/com/baeldung/setterdi/domain/Car.java @@ -0,0 +1,34 @@ +package com.baeldung.setterdi.domain; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class Car { + private Engine engine; + private Transmission transmission; + private Trailer trailer; + + public Car() { + } + + @Autowired + public void setEngine(Engine engine) { + this.engine = engine; + } + + @Autowired + public void setTransmission(Transmission transmission) { + this.transmission = transmission; + } + + @Autowired + public void setTrailer(Trailer trailer) { + this.trailer = trailer; + } + + @Override + public String toString() { + return String.format("Engine: %s Transmission: %s Trailer: %s", engine, transmission, trailer); + } +} diff --git a/spring-core/src/main/java/com/baeldung/setterdi/domain/Engine.java b/spring-core/src/main/java/com/baeldung/setterdi/domain/Engine.java new file mode 100644 index 000000000000..50c247cacf45 --- /dev/null +++ b/spring-core/src/main/java/com/baeldung/setterdi/domain/Engine.java @@ -0,0 +1,22 @@ +package com.baeldung.setterdi.domain; + +public class Engine { + private String type; + private int volume; + + public Engine() { + } + + public void setType(String type) { + this.type = type; + } + + public void setVolume(int volume) { + this.volume = volume; + } + + @Override + public String toString() { + return String.format("%s %d", type, volume); + } +} \ No newline at end of file diff --git a/spring-core/src/main/java/com/baeldung/setterdi/domain/Trailer.java b/spring-core/src/main/java/com/baeldung/setterdi/domain/Trailer.java new file mode 100644 index 000000000000..616732309eed --- /dev/null +++ b/spring-core/src/main/java/com/baeldung/setterdi/domain/Trailer.java @@ -0,0 +1,11 @@ +package com.baeldung.setterdi.domain; + +public class Trailer { + public Trailer() { + } + + @Override + public String toString() { + return "Trailer"; + } +} diff --git a/spring-core/src/main/java/com/baeldung/setterdi/domain/Transmission.java b/spring-core/src/main/java/com/baeldung/setterdi/domain/Transmission.java new file mode 100644 index 000000000000..18b62afdc1e4 --- /dev/null +++ b/spring-core/src/main/java/com/baeldung/setterdi/domain/Transmission.java @@ -0,0 +1,17 @@ +package com.baeldung.setterdi.domain; + +public class Transmission { + private String type; + + public Transmission() { + } + + public void setType(String type) { + this.type = type; + } + + @Override + public String toString() { + return String.format("%s", type); + } +} diff --git a/spring-core/src/main/java/com/baeldung/value/SomeBean.java b/spring-core/src/main/java/com/baeldung/value/SomeBean.java new file mode 100644 index 000000000000..39d5245049e9 --- /dev/null +++ b/spring-core/src/main/java/com/baeldung/value/SomeBean.java @@ -0,0 +1,17 @@ +package com.baeldung.value; + +public class SomeBean { + private int someValue; + + public SomeBean(int someValue) { + this.someValue = someValue; + } + + public int getSomeValue() { + return someValue; + } + + public void setSomeValue(int someValue) { + this.someValue = someValue; + } +} diff --git a/spring-core/src/main/java/com/baeldung/value/ValuesApp.java b/spring-core/src/main/java/com/baeldung/value/ValuesApp.java new file mode 100644 index 000000000000..f525dfde8949 --- /dev/null +++ b/spring-core/src/main/java/com/baeldung/value/ValuesApp.java @@ -0,0 +1,73 @@ +package com.baeldung.value; + +import java.util.Arrays; +import java.util.List; + +import javax.annotation.PostConstruct; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; + +@Configuration +@PropertySource(name = "myProperties", value = "values.properties") +public class ValuesApp { + + @Value("string value") + private String stringValue; + + @Value("${value.from.file}") + private String valueFromFile; + + @Value("${systemValue}") + private String systemValue; + + @Value("${unknown_param:some default}") + private String someDefault; + + @Value("${priority}") + private String prioritySystemProperty; + + @Value("${listOfValues}") + private String[] valuesArray; + + @Value("#{systemProperties['priority']}") + private String spelValue; + + @Value("#{systemProperties['unknown'] ?: 'some default'}") + private String spelSomeDefault; + + @Value("#{someBean.someValue}") + private Integer someBeanValue; + + @Value("#{'${listOfValues}'.split(',')}") + private List valuesList; + + public static void main(String[] args) { + System.setProperty("systemValue", "Some system parameter value"); + System.setProperty("priority", "System property"); + ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ValuesApp.class); + } + + @Bean + public SomeBean someBean() { + return new SomeBean(10); + } + + @PostConstruct + public void afterInitialize() { + System.out.println(stringValue); + System.out.println(valueFromFile); + System.out.println(systemValue); + System.out.println(someDefault); + System.out.println(prioritySystemProperty); + System.out.println(Arrays.toString(valuesArray)); + System.out.println(spelValue); + System.out.println(spelSomeDefault); + System.out.println(someBeanValue); + System.out.println(valuesList); + } +} diff --git a/spring-core/src/main/resources/beaninjectiontypes-context.xml b/spring-core/src/main/resources/beaninjectiontypes-context.xml new file mode 100644 index 000000000000..dfdea41cdce8 --- /dev/null +++ b/spring-core/src/main/resources/beaninjectiontypes-context.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-core/src/main/resources/constructordi.xml b/spring-core/src/main/resources/constructordi.xml new file mode 100644 index 000000000000..231e72adcb49 --- /dev/null +++ b/spring-core/src/main/resources/constructordi.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + diff --git a/spring-core/src/main/resources/setterdi.xml b/spring-core/src/main/resources/setterdi.xml new file mode 100644 index 000000000000..8b7f0d41e725 --- /dev/null +++ b/spring-core/src/main/resources/setterdi.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-core/src/main/resources/values.properties b/spring-core/src/main/resources/values.properties new file mode 100644 index 000000000000..d7d61b8ee8e1 --- /dev/null +++ b/spring-core/src/main/resources/values.properties @@ -0,0 +1,3 @@ +value.from.file=Value got from the file +priority=Properties file +listOfValues=A,B,C \ No newline at end of file diff --git a/spring-core/src/test/java/com/baeldung/applicationcontext/ClasspathXmlApplicationContextTest.java b/spring-core/src/test/java/com/baeldung/applicationcontext/ClasspathXmlApplicationContextIntegrationTest.java similarity index 97% rename from spring-core/src/test/java/com/baeldung/applicationcontext/ClasspathXmlApplicationContextTest.java rename to spring-core/src/test/java/com/baeldung/applicationcontext/ClasspathXmlApplicationContextIntegrationTest.java index 8ee280a2e8e5..d49f63aea675 100644 --- a/spring-core/src/test/java/com/baeldung/applicationcontext/ClasspathXmlApplicationContextTest.java +++ b/spring-core/src/test/java/com/baeldung/applicationcontext/ClasspathXmlApplicationContextIntegrationTest.java @@ -1,18 +1,18 @@ package com.baeldung.applicationcontext; -import static org.hamcrest.core.IsEqual.equalTo; -import static org.junit.Assert.assertThat; - -import java.util.List; -import java.util.Locale; - import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.MessageSource; import org.springframework.context.support.ClassPathXmlApplicationContext; -public class ClasspathXmlApplicationContextTest { +import java.util.List; +import java.util.Locale; + +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertThat; + +public class ClasspathXmlApplicationContextIntegrationTest { @Test public void testBasicUsage() { ApplicationContext context = new ClassPathXmlApplicationContext("classpathxmlapplicationcontext-example.xml"); diff --git a/spring-core/src/test/java/com/baeldung/test/beaninjection/BeanInjectionJavaConfigTest.java b/spring-core/src/test/java/com/baeldung/test/beaninjection/BeanInjectionJavaConfigTest.java new file mode 100644 index 000000000000..4befd884eb0f --- /dev/null +++ b/spring-core/src/test/java/com/baeldung/test/beaninjection/BeanInjectionJavaConfigTest.java @@ -0,0 +1,50 @@ +package com.baeldung.test.beaninjection; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.baeldung.beaninjection.AnotherSampleDAOBean; +import com.baeldung.beaninjection.ExampleDAOBean; +import com.baeldung.beaninjection.ExampleServiceBean; +import com.baeldung.configuration.ApplicationContextTestBeanInjectionTypes; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = ApplicationContextTestBeanInjectionTypes.class) +public class BeanInjectionJavaConfigTest implements ApplicationContextAware { + + private ApplicationContext beanInjectedContext; + + @Test + public void testDAOInjectionByJava() { + ExampleServiceBean serviceBean = beanInjectedContext.getBean(ExampleServiceBean.class); + assertNotNull("Failed: Constructor Injection,Bean Reference Injection,Java Config, ExampleServiceBean", serviceBean.getExampleDAO()); + assertNotNull("Failed: Constructor Injection,Bean Reference Injection,Java Config, ExampleServiceBean", serviceBean.getAnotherSampleDAO()); + assertTrue("Failed: Constructor Injection,String Property , Java Config", serviceBean.getPropertyX() + .equals("Some Service Property X")); + } + + @Test + public void testPropertyInjectioninDAOByJava() { + ExampleDAOBean daoBean = beanInjectedContext.getBean(ExampleDAOBean.class); + assertTrue("Failed: Constructor Injection,String Property , Java Config", daoBean.getPropertyX() + .equals("Mandatory DAO Property X")); + + AnotherSampleDAOBean anotherDAOBean = beanInjectedContext.getBean(AnotherSampleDAOBean.class); + assertTrue("Failed: Constructor Injection,String Property , XML Config", anotherDAOBean.getPropertyY() + .equals("Mandatory DAO Property Y")); + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + // TODO Auto-generated method stub + this.beanInjectedContext = applicationContext; + } +} diff --git a/spring-core/src/test/java/com/baeldung/test/beaninjection/BeanInjectionXMLConfigTest.java b/spring-core/src/test/java/com/baeldung/test/beaninjection/BeanInjectionXMLConfigTest.java new file mode 100644 index 000000000000..d19a099aad5d --- /dev/null +++ b/spring-core/src/test/java/com/baeldung/test/beaninjection/BeanInjectionXMLConfigTest.java @@ -0,0 +1,49 @@ +package com.baeldung.test.beaninjection; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.baeldung.beaninjection.AnotherSampleDAOBean; +import com.baeldung.beaninjection.ExampleDAOBean; +import com.baeldung.beaninjection.ExampleServiceBean; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = "classpath:beaninjectiontypes-context.xml") +public class BeanInjectionXMLConfigTest implements ApplicationContextAware { + + private ApplicationContext beanInjectedContext; + + @Test + public void testDAOInjectionByXML() { + ExampleServiceBean serviceBean = beanInjectedContext.getBean(ExampleServiceBean.class); + assertNotNull("Failed: Constructor Injection,Bean Reference Injection,XML Config, ExampleServiceBean", serviceBean.getExampleDAO()); + assertNotNull("Failed: Constructor Injection,Bean Reference Injection,XML Config, ExampleServiceBean", serviceBean.getAnotherSampleDAO()); + assertTrue("Failed: Constructor Injection,String Property , XML Config", serviceBean.getPropertyX() + .equals("Some Service Property X")); + } + + @Test + public void testPropertyInjectioninDAOByXML() { + ExampleDAOBean daoBean = beanInjectedContext.getBean(ExampleDAOBean.class); + assertTrue("Failed: Constructor Injection,String Property , XML Config", daoBean.getPropertyX() + .equals("Mandatory DAO Property X")); + + AnotherSampleDAOBean anotherDAOBean = beanInjectedContext.getBean(AnotherSampleDAOBean.class); + assertTrue("Failed: Constructor Injection,String Property , XML Config", anotherDAOBean.getPropertyY() + .equals("Mandatory DAO Property Y")); + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + // TODO Auto-generated method stub + this.beanInjectedContext = applicationContext; + } +} diff --git a/spring-boot-servlet/.gitignore b/spring-custom-aop/spring-custom-aop/.gitignore similarity index 100% rename from spring-boot-servlet/.gitignore rename to spring-custom-aop/spring-custom-aop/.gitignore diff --git a/spring-custom-aop/spring-custom-aop/README.MD b/spring-custom-aop/spring-custom-aop/README.MD new file mode 100644 index 000000000000..9fe18aaacc73 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/README.MD @@ -0,0 +1,13 @@ +###The Course +The "REST With Spring" Classes: http://bit.ly/restwithspring + +###Relevant Articles: +- [Quick Guide to @RestClientTest in Spring Boot](http://www.baeldung.com/restclienttest-in-spring-boot) +- [Intro to Spring Boot Starters](http://www.baeldung.com/spring-boot-starters) +- [A Guide to Spring in Eclipse STS](http://www.baeldung.com/eclipse-sts-spring) +- [Introduction to WebJars](http://www.baeldung.com/maven-webjars) +- [Create a Fat Jar App with Spring Boot](http://www.baeldung.com/deployable-fat-jar-spring-boot) +- [The @ServletComponentScan Annotation in Spring Boot](http://www.baeldung.com/spring-servletcomponentscan) +- [A Custom Data Binder in Spring MVC](http://www.baeldung.com/spring-mvc-custom-data-binder) +- [Intro to Building an Application with Spring Boot](http://www.baeldung.com/intro-to-spring-boot) +- [How to Register a Servlet in a Java Web Application](http://www.baeldung.com/register-servlet) diff --git a/spring-custom-aop/spring-custom-aop/pom.xml b/spring-custom-aop/spring-custom-aop/pom.xml new file mode 100644 index 000000000000..bab6f1f10154 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/pom.xml @@ -0,0 +1,224 @@ + + 4.0.0 + com.baeldung + spring-boot + 0.0.1-SNAPSHOT + war + spring-boot + This is simple boot application for Spring boot actuator test + + + + org.springframework.boot + spring-boot-starter-parent + 1.5.2.RELEASE + + + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.springframework.boot + spring-boot-starter-security + + + + org.springframework.boot + spring-boot-starter-tomcat + provided + + + + org.apache.tomcat.embed + tomcat-embed-core + ${tomcat.version} + + + + org.apache.tomcat.embed + tomcat-embed-jasper + ${tomcat.version} + + + + io.dropwizard.metrics + metrics-core + + + + com.h2database + h2 + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.boot + spring-boot-starter + + + com.jayway.jsonpath + json-path + test + + + org.springframework.boot + spring-boot-starter-mail + + + org.subethamail + subethasmtp + ${subethasmtp.version} + test + + + + org.webjars + bootstrap + ${bootstrap.version} + + + org.webjars + jquery + ${jquery.version} + + + + com.google.guava + guava + 18.0 + + + + org.apache.tomcat + tomcat-servlet-api + ${tomee-servlet-api.version} + provided + + + + + + spring-boot + + + src/main/resources + true + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + + org.apache.maven.plugins + maven-war-plugin + + + + pl.project13.maven + git-commit-id-plugin + ${git-commit-id-plugin.version} + + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*IntegrationTest.java + **/*LiveTest.java + + + + + + + + + + + integration + + + + org.apache.maven.plugins + maven-surefire-plugin + + + integration-test + + test + + + + **/*LiveTest.java + + + **/*IntegrationTest.java + + + + + + + json + + + + + + + + + + + + org.baeldung.boot.DemoApplication + UTF-8 + 1.8 + 4.3.4.RELEASE + 2.2.1 + 3.1.1 + 3.3.7-1 + 3.1.7 + 8.5.11 + + + diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/annotation/servletcomponentscan/SpringBootAnnotatedApp.java b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/annotation/servletcomponentscan/SpringBootAnnotatedApp.java new file mode 100644 index 000000000000..b4d416dd9623 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/annotation/servletcomponentscan/SpringBootAnnotatedApp.java @@ -0,0 +1,25 @@ +package com.baeldung.annotation.servletcomponentscan; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.servlet.ServletComponentScan; + +/** + * using the following annotations are equivalent: + *
  • + * @ServletComponentScan + *
  • + * @ServletComponentScan(basePackages = "com.baeldung.annotation.servletcomponentscan.components") + *
  • + * @ServletComponentScan(basePackageClasses = {AttrListener.class, HelloFilter.class, HelloServlet.class, EchoServlet.class}) + *
+ */ +@SpringBootApplication +@ServletComponentScan("com.baeldung.annotation.servletcomponentscan.components") +public class SpringBootAnnotatedApp { + + public static void main(String[] args) { + SpringApplication.run(SpringBootAnnotatedApp.class, args); + } + +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/annotation/servletcomponentscan/SpringBootPlainApp.java b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/annotation/servletcomponentscan/SpringBootPlainApp.java new file mode 100644 index 000000000000..8a39078aac78 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/annotation/servletcomponentscan/SpringBootPlainApp.java @@ -0,0 +1,13 @@ +package com.baeldung.annotation.servletcomponentscan; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan(basePackages = "com.baeldung.annotation.servletcomponentscan.components") +public class SpringBootPlainApp { + + public static void main(String[] args) { + } + +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/annotation/servletcomponentscan/components/AttrListener.java b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/annotation/servletcomponentscan/components/AttrListener.java new file mode 100644 index 000000000000..bad39c52c48f --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/annotation/servletcomponentscan/components/AttrListener.java @@ -0,0 +1,23 @@ +package com.baeldung.annotation.servletcomponentscan.components; + +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; +import javax.servlet.annotation.WebListener; + +@WebListener +public class AttrListener implements ServletContextListener { + + @Override + public void contextInitialized(ServletContextEvent servletContextEvent) { + servletContextEvent + .getServletContext() + .setAttribute("servlet-context-attr", "test"); + System.out.println("context init"); + } + + @Override + public void contextDestroyed(ServletContextEvent servletContextEvent) { + System.out.println("context destroy"); + } + +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/annotation/servletcomponentscan/components/EchoServlet.java b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/annotation/servletcomponentscan/components/EchoServlet.java new file mode 100644 index 000000000000..3419cd0eaf02 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/annotation/servletcomponentscan/components/EchoServlet.java @@ -0,0 +1,29 @@ +package com.baeldung.annotation.servletcomponentscan.components; + +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; + +@WebServlet(name = "echo servlet", urlPatterns = "/echo") +public class EchoServlet extends HttpServlet { + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) { + try { + Path path = File + .createTempFile("echo", "tmp") + .toPath(); + Files.copy(request.getInputStream(), path, StandardCopyOption.REPLACE_EXISTING); + Files.copy(path, response.getOutputStream()); + Files.delete(path); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/annotation/servletcomponentscan/components/HelloFilter.java b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/annotation/servletcomponentscan/components/HelloFilter.java new file mode 100644 index 000000000000..dc2368c5b272 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/annotation/servletcomponentscan/components/HelloFilter.java @@ -0,0 +1,32 @@ +package com.baeldung.annotation.servletcomponentscan.components; + +import javax.servlet.*; +import javax.servlet.annotation.WebFilter; +import javax.servlet.annotation.WebInitParam; +import java.io.IOException; + +@WebFilter(urlPatterns = "/hello", description = "a filter for hello servlet", initParams = { @WebInitParam(name = "msg", value = "filtering ") }, filterName = "hello filter", servletNames = { "echo servlet" }) +public class HelloFilter implements Filter { + + private FilterConfig filterConfig; + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + System.out.println("filter init"); + this.filterConfig = filterConfig; + } + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + servletResponse + .getOutputStream() + .print(filterConfig.getInitParameter("msg")); + filterChain.doFilter(servletRequest, servletResponse); + } + + @Override + public void destroy() { + System.out.println("filter destroy"); + } + +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/annotation/servletcomponentscan/components/HelloServlet.java b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/annotation/servletcomponentscan/components/HelloServlet.java new file mode 100644 index 000000000000..aeae7aecc949 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/annotation/servletcomponentscan/components/HelloServlet.java @@ -0,0 +1,32 @@ +package com.baeldung.annotation.servletcomponentscan.components; + +import javax.servlet.ServletConfig; +import javax.servlet.annotation.WebInitParam; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@WebServlet(urlPatterns = "/hello", initParams = { @WebInitParam(name = "msg", value = "hello")}) +public class HelloServlet extends HttpServlet { + + private ServletConfig servletConfig; + + @Override + public void init(ServletConfig servletConfig){ + this.servletConfig = servletConfig; + } + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) { + try { + response + .getOutputStream() + .write(servletConfig.getInitParameter("msg").getBytes()); + } catch (IOException e) { + e.printStackTrace(); + } + } + +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/git/CommitIdApplication.java b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/git/CommitIdApplication.java new file mode 100644 index 000000000000..cd696eae70dd --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/git/CommitIdApplication.java @@ -0,0 +1,23 @@ +package com.baeldung.git; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; +import org.springframework.core.io.ClassPathResource; + +@SpringBootApplication(scanBasePackages = { "com.baeldung.git" }) +public class CommitIdApplication { + public static void main(String[] args) { + SpringApplication.run(CommitIdApplication.class, args); + } + + @Bean + public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() { + PropertySourcesPlaceholderConfigurer c = new PropertySourcesPlaceholderConfigurer(); + c.setLocation(new ClassPathResource("git.properties")); + c.setIgnoreResourceNotFound(true); + c.setIgnoreUnresolvablePlaceholders(true); + return c; + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/git/CommitInfoController.java b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/git/CommitInfoController.java new file mode 100644 index 000000000000..6d44e02ec2f5 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/git/CommitInfoController.java @@ -0,0 +1,30 @@ +package com.baeldung.git; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.Map; + +@RestController +public class CommitInfoController { + + @Value("${git.commit.message.short}") + private String commitMessage; + + @Value("${git.branch}") + private String branch; + + @Value("${git.commit.id}") + private String commitId; + + @RequestMapping("/commitId") + public Map getCommitId() { + Map result = new HashMap<>(); + result.put("Commit message", commitMessage); + result.put("Commit branch", branch); + result.put("Commit id", commitId); + return result; + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/git/README.md b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/git/README.md new file mode 100644 index 000000000000..7e6a597c2845 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/git/README.md @@ -0,0 +1,2 @@ +### Relevant Articles: +- [Injecting Git Information Into Spring](http://www.baeldung.com/spring-git-information) diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/internationalization/InternationalizationApp.java b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/internationalization/InternationalizationApp.java new file mode 100644 index 000000000000..c92d1c32e632 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/internationalization/InternationalizationApp.java @@ -0,0 +1,15 @@ +package com.baeldung.internationalization; + +import javax.annotation.security.RolesAllowed; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class InternationalizationApp { + @RolesAllowed("*") + public static void main(String[] args) { + System.setProperty("security.basic.enabled", "false"); + SpringApplication.run(InternationalizationApp.class, args); + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/internationalization/config/MvcConfig.java b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/internationalization/config/MvcConfig.java new file mode 100644 index 000000000000..59f7fd3ba53e --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/internationalization/config/MvcConfig.java @@ -0,0 +1,38 @@ +package com.baeldung.internationalization.config; + +import java.util.Locale; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.LocaleResolver; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; +import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; +import org.springframework.web.servlet.i18n.SessionLocaleResolver; + +@Configuration +@EnableWebMvc +@ComponentScan(basePackages = "com.baeldung.internationalization.config") +public class MvcConfig extends WebMvcConfigurerAdapter { + + @Bean + public LocaleResolver localeResolver() { + SessionLocaleResolver slr = new SessionLocaleResolver(); + slr.setDefaultLocale(Locale.US); + return slr; + } + + @Bean + public LocaleChangeInterceptor localeChangeInterceptor() { + LocaleChangeInterceptor lci = new LocaleChangeInterceptor(); + lci.setParamName("lang"); + return lci; + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(localeChangeInterceptor()); + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/internationalization/config/PageController.java b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/internationalization/config/PageController.java new file mode 100644 index 000000000000..96a534b85377 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/internationalization/config/PageController.java @@ -0,0 +1,14 @@ +package com.baeldung.internationalization.config; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +public class PageController { + + @GetMapping("/international") + public String getInternationalPage() { + return "international"; + } + +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/intro/App.java b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/intro/App.java new file mode 100644 index 000000000000..3db5d3256ed2 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/intro/App.java @@ -0,0 +1,13 @@ +package com.baeldung.intro; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class App +{ + public static void main( String[] args ) + { + SpringApplication.run(App.class, args); + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/intro/controller/HomeController.java b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/intro/controller/HomeController.java new file mode 100644 index 000000000000..2a7111135c7a --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/intro/controller/HomeController.java @@ -0,0 +1,18 @@ +package com.baeldung.intro.controller; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class HomeController { + + @RequestMapping("/") + public String root(){ + return "Index Page"; + } + + @RequestMapping("/local") + public String local(){ + return "/local"; + } +} diff --git a/spring-boot-servlet/src/main/java/com/baeldung/ApplicationMain.java b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/ApplicationMain.java similarity index 95% rename from spring-boot-servlet/src/main/java/com/baeldung/ApplicationMain.java rename to spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/ApplicationMain.java index 66f2e85999c3..a6ea3757fe30 100644 --- a/spring-boot-servlet/src/main/java/com/baeldung/ApplicationMain.java +++ b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/ApplicationMain.java @@ -1,4 +1,4 @@ -package com.baeldung; +package com.baeldung.servlets; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -16,4 +16,4 @@ public static void main(String[] args) { protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(ApplicationMain.class); } -} \ No newline at end of file +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/configuration/WebAppInitializer.java b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/configuration/WebAppInitializer.java new file mode 100644 index 000000000000..eadd40355a92 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/configuration/WebAppInitializer.java @@ -0,0 +1,32 @@ +package com.baeldung.servlets.configuration; + +import org.springframework.web.WebApplicationInitializer; +import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; +import org.springframework.web.context.support.XmlWebApplicationContext; +import org.springframework.web.servlet.DispatcherServlet; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRegistration; + +public class WebAppInitializer implements WebApplicationInitializer { + + public void onStartup(ServletContext container) throws ServletException { + + AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); + ctx.register(WebMvcConfigure.class); + ctx.setServletContext(container); + + ServletRegistration.Dynamic servletOne = container.addServlet("SpringProgrammaticDispatcherServlet", new DispatcherServlet(ctx)); + servletOne.setLoadOnStartup(1); + servletOne.addMapping("/"); + + XmlWebApplicationContext xctx = new XmlWebApplicationContext(); + xctx.setConfigLocation("/WEB-INF/context.xml"); + xctx.setServletContext(container); + + ServletRegistration.Dynamic servletTwo = container.addServlet("SpringProgrammaticXMLDispatcherServlet", new DispatcherServlet(xctx)); + servletTwo.setLoadOnStartup(1); + servletTwo.addMapping("/"); + } + +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/configuration/WebMvcConfigure.java b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/configuration/WebMvcConfigure.java new file mode 100644 index 000000000000..3d6a10c2aca6 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/configuration/WebMvcConfigure.java @@ -0,0 +1,39 @@ +package com.baeldung.servlets.configuration; + +import org.springframework.boot.web.support.ErrorPageFilter; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.ViewResolver; +import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; +import org.springframework.web.servlet.resource.PathResourceResolver; +import org.springframework.web.servlet.view.InternalResourceViewResolver; + +@Configuration +public class WebMvcConfigure extends WebMvcConfigurerAdapter { + + @Bean + public ViewResolver getViewResolver() { + InternalResourceViewResolver resolver = new InternalResourceViewResolver(); + resolver.setPrefix("/WEB-INF/"); + resolver.setSuffix(".jsp"); + return resolver; + } + + @Override + public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { + configurer.enable(); + } + + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("/resources/**").addResourceLocations("/resources/").setCachePeriod(3600).resourceChain(true).addResolver(new PathResourceResolver()); + } + + @Bean + public ErrorPageFilter errorPageFilter() { + return new ErrorPageFilter(); + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/props/Constants.java b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/props/Constants.java new file mode 100644 index 000000000000..6345d1f96956 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/props/Constants.java @@ -0,0 +1,20 @@ +package com.baeldung.servlets.props; + +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.Properties; + +public final class Constants { + + @Autowired + PropertySourcesLoader psl; + + public static final String breakLine = System.getProperty("line.separator"); + private static final PropertyLoader pl = new PropertyLoader(); + private static final Properties mainProps = pl.getProperties("custom.properties"); + public static final String DISPATCHER_SERVLET_NAME = mainProps.getProperty("dispatcher.servlet.name"); + public static final String DISPATCHER_SERVLET_MAPPING = mainProps.getProperty("dispatcher.servlet.mapping"); + private final String EXAMPLE_SERVLET_NAME = psl.getProperty("example.servlet.name"); + private final String EXAMPLE_SERVLET_MAPPING = psl.getProperty("example.servlet.mapping"); + +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/props/PropertyLoader.java b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/props/PropertyLoader.java new file mode 100644 index 000000000000..c29da4592951 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/props/PropertyLoader.java @@ -0,0 +1,27 @@ +package com.baeldung.servlets.props; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +public class PropertyLoader { + private static final Logger log = LoggerFactory.getLogger(PropertyLoader.class); + + public Properties getProperties(String file) { + Properties prop = new Properties(); + InputStream input = null; + try { + input = getClass().getResourceAsStream(file); + prop.load(input); + if (input != null) { + input.close(); + } + } catch (IOException ex) { + log.error("IOException: " + ex); + } + return prop; + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/props/PropertySourcesLoader.java b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/props/PropertySourcesLoader.java new file mode 100644 index 000000000000..56a6751326c7 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/props/PropertySourcesLoader.java @@ -0,0 +1,23 @@ +package com.baeldung.servlets.props; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import org.springframework.core.env.ConfigurableEnvironment; + +@Configuration +@ComponentScan(basePackages = { "com.baeldung.*" }) +@PropertySource("classpath:custom.properties") public class PropertySourcesLoader { + + private static final Logger log = LoggerFactory.getLogger(PropertySourcesLoader.class); + + @Autowired + ConfigurableEnvironment env; + + public String getProperty(String key) { + return env.getProperty(key); + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/servlets/GenericCustomServlet.java b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/servlets/GenericCustomServlet.java new file mode 100644 index 000000000000..49dd9404b7d1 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/servlets/GenericCustomServlet.java @@ -0,0 +1,18 @@ +package com.baeldung.servlets.servlets; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; + +public class GenericCustomServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/html"); + PrintWriter out = response.getWriter(); + out.println("

Hello World

"); + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/servlets/javaee/AnnotationServlet.java b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/servlets/javaee/AnnotationServlet.java new file mode 100644 index 000000000000..b50a7d545405 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/servlets/javaee/AnnotationServlet.java @@ -0,0 +1,20 @@ +package com.baeldung.servlets.servlets.javaee; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@WebServlet(name = "AnnotationServlet", + description = "Example Servlet Using Annotations", + urlPatterns = { "/annotationservlet" }) +public class AnnotationServlet extends HttpServlet { + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + request.getRequestDispatcher("/annotationservlet.jsp").forward(request, response); + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/servlets/javaee/EEWebXmlServlet.java b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/servlets/javaee/EEWebXmlServlet.java new file mode 100644 index 000000000000..c7b373064fdf --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/servlets/javaee/EEWebXmlServlet.java @@ -0,0 +1,20 @@ +package com.baeldung.servlets.servlets.javaee; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; + +public class EEWebXmlServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/html"); + PrintWriter out = response.getWriter(); + out.println("

Hello World

"); + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/servlets/springboot/SpringRegistrationBeanServlet.java b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/servlets/springboot/SpringRegistrationBeanServlet.java new file mode 100644 index 000000000000..e3c225d429ff --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/servlets/springboot/SpringRegistrationBeanServlet.java @@ -0,0 +1,17 @@ +package com.baeldung.servlets.servlets.springboot; + +import com.baeldung.servlets.servlets.GenericCustomServlet; +import org.springframework.boot.web.servlet.ServletRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SpringRegistrationBeanServlet { + + @Bean + public ServletRegistrationBean genericCustomServlet() { + ServletRegistrationBean bean = new ServletRegistrationBean(new GenericCustomServlet(), "/springregistrationbeanservlet/*"); + bean.setLoadOnStartup(1); + return bean; + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/servlets/springboot/embedded/EmbeddedTomcatExample.java b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/servlets/springboot/embedded/EmbeddedTomcatExample.java new file mode 100644 index 000000000000..9e460d03a893 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/servlets/servlets/springboot/embedded/EmbeddedTomcatExample.java @@ -0,0 +1,16 @@ +package com.baeldung.servlets.servlets.springboot.embedded; + +import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory; +import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class EmbeddedTomcatExample { + + @Bean + public EmbeddedServletContainerFactory servletContainer() { + TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory(); + return tomcat; + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/utils/Application.java b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/utils/Application.java new file mode 100644 index 000000000000..a3d9f9130c32 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/utils/Application.java @@ -0,0 +1,18 @@ +package com.baeldung.utils; + +import javax.annotation.security.RolesAllowed; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan(basePackages="com.baeldung.utils") +public class Application { + + @RolesAllowed("*") + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/utils/controller/UtilsController.java b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/utils/controller/UtilsController.java new file mode 100644 index 000000000000..7b4827cdf292 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/utils/controller/UtilsController.java @@ -0,0 +1,49 @@ +package com.baeldung.utils.controller; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.ServletRequestUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.util.WebUtils; + +@Controller +public class UtilsController { + + @GetMapping("/utils") + public String webUtils(Model model) { + return "utils"; + } + + @PostMapping("/setParam") + public String post(HttpServletRequest request, Model model) { + String param = ServletRequestUtils.getStringParameter(request, "param", "DEFAULT"); + +// Long param = ServletRequestUtils.getLongParameter(request, "param",1L); +// boolean param = ServletRequestUtils.getBooleanParameter(request, "param", true); +// double param = ServletRequestUtils.getDoubleParameter(request, "param", 1000); +// float param = ServletRequestUtils.getFloatParameter(request, "param", (float) 1.00); +// int param = ServletRequestUtils.getIntParameter(request, "param", 100); + +// try { +// ServletRequestUtils.getRequiredStringParameter(request, "param"); +// } catch (ServletRequestBindingException e) { +// e.printStackTrace(); +// } + + WebUtils.setSessionAttribute(request, "parameter", param); + model.addAttribute("parameter", "You set: "+(String) WebUtils.getSessionAttribute(request, "parameter")); + return "utils"; + } + + @GetMapping("/other") + public String other(HttpServletRequest request, Model model) { + String param = (String) WebUtils.getSessionAttribute(request, "parameter"); + model.addAttribute("parameter", param); + return "other"; + } + +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/webjar/TestController.java b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/webjar/TestController.java new file mode 100644 index 000000000000..e8e7fd5ce993 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/webjar/TestController.java @@ -0,0 +1,15 @@ +package com.baeldung.webjar; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; + +@Controller +public class TestController { + + @RequestMapping(value = "/") + public String welcome(Model model) { + return "index"; + } + +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/webjar/WebjarsdemoApplication.java b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/webjar/WebjarsdemoApplication.java new file mode 100644 index 000000000000..d2135754c950 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/com/baeldung/webjar/WebjarsdemoApplication.java @@ -0,0 +1,13 @@ +package com.baeldung.webjar; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +public class WebjarsdemoApplication { + + public static void main(String[] args) { + SpringApplication.run(WebjarsdemoApplication.class, args); + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/Application.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/Application.java new file mode 100644 index 000000000000..aae0c427a98a --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/Application.java @@ -0,0 +1,13 @@ +package org.baeldung; + +import org.springframework.boot.SpringApplication; +import org.springframework.context.ApplicationContext; + +@org.springframework.boot.autoconfigure.SpringBootApplication +public class Application { + private static ApplicationContext applicationContext; + + public static void main(String[] args) { + applicationContext = SpringApplication.run(Application.class, args); + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/boot/DemoApplication.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/boot/DemoApplication.java new file mode 100644 index 000000000000..e61d140396ff --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/boot/DemoApplication.java @@ -0,0 +1,14 @@ +package org.baeldung.boot; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class DemoApplication { + + public static void main(String[] args) { + System.setProperty("spring.config.name", "demo"); + SpringApplication.run(DemoApplication.class, args); + } + +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/boot/components/FooService.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/boot/components/FooService.java new file mode 100644 index 000000000000..235fd4329933 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/boot/components/FooService.java @@ -0,0 +1,21 @@ +package org.baeldung.boot.components; + +import org.baeldung.boot.model.Foo; +import org.baeldung.boot.repository.FooRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class FooService { + + @Autowired + private FooRepository fooRepository; + + public Foo getFooWithId(Integer id) throws Exception { + return fooRepository.findOne(id); + } + + public Foo getFooWithName(String name) { + return fooRepository.findByName(name); + } +} \ No newline at end of file diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/boot/exceptions/CommonException.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/boot/exceptions/CommonException.java new file mode 100644 index 000000000000..1f008440e613 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/boot/exceptions/CommonException.java @@ -0,0 +1,13 @@ +package org.baeldung.boot.exceptions; + +public class CommonException extends RuntimeException{ + + /** + * + */ + private static final long serialVersionUID = 3080004140659213332L; + + public CommonException(String message){ + super(message); + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/boot/exceptions/FooNotFoundException.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/boot/exceptions/FooNotFoundException.java new file mode 100644 index 000000000000..68ef3fa3894a --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/boot/exceptions/FooNotFoundException.java @@ -0,0 +1,13 @@ +package org.baeldung.boot.exceptions; + +public class FooNotFoundException extends RuntimeException{ + + /** + * + */ + private static final long serialVersionUID = 9042200028456133589L; + + public FooNotFoundException(String message){ + super(message); + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/boot/model/Foo.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/boot/model/Foo.java new file mode 100644 index 000000000000..ac8a8fe42959 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/boot/model/Foo.java @@ -0,0 +1,46 @@ +package org.baeldung.boot.model; + +import java.io.Serializable; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +@Entity +public class Foo implements Serializable { + private static final long serialVersionUID = 1L; + @Id + @GeneratedValue + private Integer id; + private String name; + + public Foo() { + } + + public Foo(String name) { + this.name = name; + } + + + public Foo(Integer id, String name) { + super(); + this.id = id; + this.name = name; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/boot/repository/FooRepository.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/boot/repository/FooRepository.java new file mode 100644 index 000000000000..09d6975dba29 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/boot/repository/FooRepository.java @@ -0,0 +1,8 @@ +package org.baeldung.boot.repository; + +import org.baeldung.boot.model.Foo; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface FooRepository extends JpaRepository { + public Foo findByName(String name); +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/boot/service/FooController.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/boot/service/FooController.java new file mode 100644 index 000000000000..834fa342e25b --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/boot/service/FooController.java @@ -0,0 +1,26 @@ +package org.baeldung.boot.service; + +import org.baeldung.boot.components.FooService; +import org.baeldung.boot.model.Foo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class FooController { + + @Autowired + private FooService fooService; + + @GetMapping("/{id}") + public Foo getFooWithId(@PathVariable Integer id) throws Exception { + return fooService.getFooWithId(id); + } + + @GetMapping("/") + public Foo getFooWithName(@RequestParam String name) throws Exception { + return fooService.getFooWithName(name); + } +} \ No newline at end of file diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/client/Details.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/client/Details.java new file mode 100644 index 000000000000..2ae3adc38f05 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/client/Details.java @@ -0,0 +1,32 @@ +package org.baeldung.client; + +public class Details { + + private String name; + + private String login; + + public Details() { + } + + public Details(String name, String login) { + this.name = name; + this.login = login; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/client/DetailsServiceClient.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/client/DetailsServiceClient.java new file mode 100644 index 000000000000..51fa7c6181b6 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/client/DetailsServiceClient.java @@ -0,0 +1,20 @@ +package org.baeldung.client; + +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +@Service +public class DetailsServiceClient { + + private final RestTemplate restTemplate; + + public DetailsServiceClient(RestTemplateBuilder restTemplateBuilder) { + restTemplate = restTemplateBuilder.build(); + } + + public Details getUserDetails(String name) { + return restTemplate.getForObject("/{name}/details", Details.class, name); + } + +} \ No newline at end of file diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/common/error/MyCustomErrorController.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/common/error/MyCustomErrorController.java new file mode 100644 index 000000000000..a50b88f94b08 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/common/error/MyCustomErrorController.java @@ -0,0 +1,24 @@ +package org.baeldung.common.error; + +import org.springframework.boot.autoconfigure.web.ErrorController; +import org.springframework.web.bind.annotation.RequestMapping; + +public class MyCustomErrorController implements ErrorController { + + private static final String PATH = "/error"; + + public MyCustomErrorController() { + // TODO Auto-generated constructor stub + } + + @RequestMapping(value = PATH) + public String error() { + return "Error heaven"; + } + + @Override + public String getErrorPath() { + return PATH; + } + +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/common/error/SpringHelloServletRegistrationBean.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/common/error/SpringHelloServletRegistrationBean.java new file mode 100644 index 000000000000..774cf1b97032 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/common/error/SpringHelloServletRegistrationBean.java @@ -0,0 +1,15 @@ +package org.baeldung.common.error; + +import org.springframework.boot.web.servlet.ServletRegistrationBean; + +import javax.servlet.Servlet; + +public class SpringHelloServletRegistrationBean extends ServletRegistrationBean { + + public SpringHelloServletRegistrationBean() { + } + + public SpringHelloServletRegistrationBean(Servlet servlet, String... urlMappings) { + super(servlet, urlMappings); + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/common/error/controller/ErrorController.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/common/error/controller/ErrorController.java new file mode 100644 index 000000000000..9e63418a0213 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/common/error/controller/ErrorController.java @@ -0,0 +1,22 @@ +package org.baeldung.common.error.controller; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class ErrorController { + + public ErrorController() { + } + + @RequestMapping("/400") + String error400() { + return "Error Code: 400 occured."; + } + + @RequestMapping("/errorHeaven") + String errorHeaven() { + return "You have reached the heaven of errors!!!"; + } + +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/common/properties/MyServletContainerCustomizationBean.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/common/properties/MyServletContainerCustomizationBean.java new file mode 100644 index 000000000000..3d239f894448 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/common/properties/MyServletContainerCustomizationBean.java @@ -0,0 +1,25 @@ +package org.baeldung.common.properties; + +import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer; +import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer; +import org.springframework.boot.web.servlet.ErrorPage; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; + +@Component +public class MyServletContainerCustomizationBean implements EmbeddedServletContainerCustomizer { + + public MyServletContainerCustomizationBean() { + + } + + @Override + public void customize(ConfigurableEmbeddedServletContainer container) { + container.setPort(8084); + container.setContextPath("/springbootapp"); + + container.addErrorPages(new ErrorPage(HttpStatus.BAD_REQUEST, "/400")); + container.addErrorPages(new ErrorPage("/errorHeaven")); + } + +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/common/resources/ExecutorServiceExitCodeGenerator.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/common/resources/ExecutorServiceExitCodeGenerator.java new file mode 100644 index 000000000000..07f57ec1efa3 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/common/resources/ExecutorServiceExitCodeGenerator.java @@ -0,0 +1,29 @@ +package org.baeldung.common.resources; + +import java.util.Objects; +import java.util.concurrent.ExecutorService; + +import org.springframework.boot.ExitCodeGenerator; + +public class ExecutorServiceExitCodeGenerator implements ExitCodeGenerator { + + private ExecutorService executorService; + + public ExecutorServiceExitCodeGenerator(ExecutorService executorService) { + } + + @Override + public int getExitCode() { + int returnCode = 0; + try { + if (!Objects.isNull(executorService)) { + executorService.shutdownNow(); + returnCode = 1; + } + } catch (SecurityException ex) { + returnCode = 0; + } + return returnCode; + } + +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/config/WebConfig.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/config/WebConfig.java new file mode 100644 index 000000000000..4ef407823e4a --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/config/WebConfig.java @@ -0,0 +1,17 @@ +package org.baeldung.config; + +import org.baeldung.web.resolver.HeaderVersionArgumentResolver; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; + +import java.util.List; + +@Configuration +public class WebConfig extends WebMvcConfigurerAdapter { + + @Override + public void addArgumentResolvers(final List argumentResolvers) { + argumentResolvers.add(new HeaderVersionArgumentResolver()); + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/controller/GenericEntityController.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/controller/GenericEntityController.java new file mode 100644 index 000000000000..7d1ad7d8997a --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/controller/GenericEntityController.java @@ -0,0 +1,59 @@ +package org.baeldung.controller; + +import org.baeldung.domain.GenericEntity; +import org.baeldung.domain.Modes; +import org.baeldung.web.resolver.Version; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +@RestController +public class GenericEntityController { + private List entityList = new ArrayList<>(); + + { + entityList.add(new GenericEntity(1l, "entity_1")); + entityList.add(new GenericEntity(2l, "entity_2")); + entityList.add(new GenericEntity(3l, "entity_3")); + entityList.add(new GenericEntity(4l, "entity_4")); + } + + @RequestMapping("/entity/all") + public List findAll() { + return entityList; + } + + @RequestMapping(value = "/entity", method = RequestMethod.POST) + public GenericEntity addEntity(GenericEntity entity) { + entityList.add(entity); + return entity; + } + + @RequestMapping("/entity/findby/{id}") + public GenericEntity findById(@PathVariable Long id) { + return entityList.stream().filter(entity -> entity.getId().equals(id)).findFirst().get(); + } + + @GetMapping("/entity/findbydate/{date}") + public GenericEntity findByDate(@PathVariable("date") LocalDateTime date) { + return entityList.stream().findFirst().get(); + } + + @GetMapping("/entity/findbymode/{mode}") + public GenericEntity findByEnum(@PathVariable("mode") Modes mode) { + return entityList.stream().findFirst().get(); + } + + @GetMapping("/entity/findbyversion") + public ResponseEntity findByVersion(@Version String version) { + return version != null ? new ResponseEntity(entityList.stream().findFirst().get(), HttpStatus.OK) : new ResponseEntity(HttpStatus.NOT_FOUND); + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/controller/servlet/HelloWorldServlet.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/controller/servlet/HelloWorldServlet.java new file mode 100644 index 000000000000..fc8fefd77e63 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/controller/servlet/HelloWorldServlet.java @@ -0,0 +1,43 @@ +package org.baeldung.controller.servlet; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Objects; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class HelloWorldServlet extends HttpServlet { + private static final long serialVersionUID = 1L; + + public HelloWorldServlet() { + super(); + } + + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + PrintWriter out = null; + try { + out = response.getWriter(); + out.println("HelloWorldServlet: GET METHOD"); + out.flush(); + } finally { + if (!Objects.isNull(out)) + out.close(); + } + } + + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + PrintWriter out = null; + try { + out = response.getWriter(); + out.println("HelloWorldServlet: POST METHOD"); + out.flush(); + } finally { + if (!Objects.isNull(out)) + out.close(); + } + } + +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/controller/servlet/SpringHelloWorldServlet.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/controller/servlet/SpringHelloWorldServlet.java new file mode 100644 index 000000000000..16cff5b1faad --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/controller/servlet/SpringHelloWorldServlet.java @@ -0,0 +1,43 @@ +package org.baeldung.controller.servlet; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Objects; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class SpringHelloWorldServlet extends HttpServlet { + private static final long serialVersionUID = 1L; + + public SpringHelloWorldServlet() { + super(); + } + + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + PrintWriter out = null; + try { + out = response.getWriter(); + out.println("SpringHelloWorldServlet: GET METHOD"); + out.flush(); + } finally { + if (!Objects.isNull(out)) + out.close(); + } + } + + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + PrintWriter out = null; + try { + out = response.getWriter(); + out.println("SpringHelloWorldServlet: POST METHOD"); + out.flush(); + } finally { + if (!Objects.isNull(out)) + out.close(); + } + } + +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/converter/StringToEnumConverterFactory.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/converter/StringToEnumConverterFactory.java new file mode 100644 index 000000000000..17c6fd06de25 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/converter/StringToEnumConverterFactory.java @@ -0,0 +1,27 @@ +package org.baeldung.converter; + +import org.springframework.core.convert.converter.Converter; +import org.springframework.core.convert.converter.ConverterFactory; +import org.springframework.stereotype.Component; + +@Component +public class StringToEnumConverterFactory implements ConverterFactory { + + private static class StringToEnumConverter implements Converter { + + private Class enumType; + + public StringToEnumConverter(Class enumType) { + this.enumType = enumType; + } + + public T convert(String source) { + return (T) Enum.valueOf(this.enumType, source.trim()); + } + } + + @Override + public Converter getConverter(final Class targetType) { + return new StringToEnumConverter(targetType); + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/converter/StringToLocalDateTimeConverter.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/converter/StringToLocalDateTimeConverter.java new file mode 100644 index 000000000000..cbb9e6ddb442 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/converter/StringToLocalDateTimeConverter.java @@ -0,0 +1,16 @@ +package org.baeldung.converter; + +import org.springframework.core.convert.converter.Converter; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +@Component +public class StringToLocalDateTimeConverter implements Converter { + + @Override + public LocalDateTime convert(final String source) { + return LocalDateTime.parse(source, DateTimeFormatter.ISO_LOCAL_DATE_TIME); + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/domain/GenericEntity.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/domain/GenericEntity.java new file mode 100644 index 000000000000..7b1d27cb661b --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/domain/GenericEntity.java @@ -0,0 +1,42 @@ +package org.baeldung.domain; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Entity +public class GenericEntity { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + private String value; + + public GenericEntity() { + } + + public GenericEntity(String value) { + this.value = value; + } + + public GenericEntity(Long id, String value) { + this.id = id; + this.value = value; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/domain/Modes.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/domain/Modes.java new file mode 100644 index 000000000000..473406ef2601 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/domain/Modes.java @@ -0,0 +1,6 @@ +package org.baeldung.domain; + +public enum Modes { + + ALPHA, BETA; +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/endpoints/CustomEndpoint.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/endpoints/CustomEndpoint.java new file mode 100644 index 000000000000..222a54c6ef58 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/endpoints/CustomEndpoint.java @@ -0,0 +1,35 @@ +package org.baeldung.endpoints; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.boot.actuate.endpoint.Endpoint; +import org.springframework.stereotype.Component; + +@Component +public class CustomEndpoint implements Endpoint> { + + public CustomEndpoint() { + + } + + public String getId() { + return "customEndpoint"; + } + + public boolean isEnabled() { + return true; + } + + public boolean isSensitive() { + return true; + } + + public List invoke() { + // Your logic to display the output + List messages = new ArrayList(); + messages.add("This is message 1"); + messages.add("This is message 2"); + return messages; + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/endpoints/ListEndpoints.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/endpoints/ListEndpoints.java new file mode 100644 index 000000000000..0add741a1fa7 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/endpoints/ListEndpoints.java @@ -0,0 +1,23 @@ +package org.baeldung.endpoints; + +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.endpoint.AbstractEndpoint; +import org.springframework.boot.actuate.endpoint.Endpoint; +import org.springframework.stereotype.Component; + +@Component +public class ListEndpoints extends AbstractEndpoint> { + private List endpoints; + + @Autowired + public ListEndpoints(List endpoints) { + super("listEndpoints"); + this.endpoints = endpoints; + } + + public List invoke() { + return this.endpoints; + } +} \ No newline at end of file diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/endpoints/MyHealthCheck.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/endpoints/MyHealthCheck.java new file mode 100644 index 000000000000..4410a02d47f2 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/endpoints/MyHealthCheck.java @@ -0,0 +1,22 @@ +package org.baeldung.endpoints; + +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.stereotype.Component; + +@Component +public class MyHealthCheck implements HealthIndicator { + + public Health health() { + int errorCode = check(); // perform some specific health check + if (errorCode != 0) { + return Health.down().withDetail("Error Code", errorCode).withDetail("Description", "You custom MyHealthCheck endpoint is down").build(); + } + return Health.up().build(); + } + + public int check() { + // Your logic to check health + return 1; + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/main/SpringBootApplication.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/main/SpringBootApplication.java new file mode 100644 index 000000000000..b828a5b841a0 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/main/SpringBootApplication.java @@ -0,0 +1,68 @@ +package org.baeldung.main; + +import org.baeldung.common.error.SpringHelloServletRegistrationBean; +import org.baeldung.common.resources.ExecutorServiceExitCodeGenerator; +import org.baeldung.controller.servlet.HelloWorldServlet; +import org.baeldung.controller.servlet.SpringHelloWorldServlet; +import org.baeldung.service.LoginService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +@RestController +@EnableAutoConfiguration +@ComponentScan({ "org.baeldung.common.error", "org.baeldung.common.error.controller", "org.baeldung.common.properties", "org.baeldung.common.resources", "org.baeldung.endpoints", "org.baeldung.service", "org.baeldung.monitor.jmx", "org.baeldung.service" }) +public class SpringBootApplication { + + private static ApplicationContext applicationContext; + + @Autowired + private LoginService service; + + @RequestMapping("/") + String home() { + service.login("admin", "admin".toCharArray()); + return "TADA!!! You are in Spring Boot Actuator test application."; + } + + public static void main(String[] args) { + applicationContext = SpringApplication.run(SpringBootApplication.class, args); + } + + @Bean + public ExecutorService executorService() { + return Executors.newFixedThreadPool(10); + } + + @Bean + public HelloWorldServlet helloWorldServlet() { + return new HelloWorldServlet(); + } + + @Bean + public SpringHelloServletRegistrationBean servletRegistrationBean() { + SpringHelloServletRegistrationBean bean = new SpringHelloServletRegistrationBean(new SpringHelloWorldServlet(), "/springHelloWorld/*"); + bean.setLoadOnStartup(1); + bean.addInitParameter("message", "SpringHelloWorldServlet special message"); + return bean; + } + + @Bean + @Autowired + public ExecutorServiceExitCodeGenerator executorServiceExitCodeGenerator(ExecutorService executorService) { + return new ExecutorServiceExitCodeGenerator(executorService); + } + + public void shutDown(ExecutorServiceExitCodeGenerator executorServiceExitCodeGenerator) { + SpringApplication.exit(applicationContext, executorServiceExitCodeGenerator); + } + +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/monitor/jmx/MonitoringConfig.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/monitor/jmx/MonitoringConfig.java new file mode 100644 index 000000000000..febe3336eb36 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/monitor/jmx/MonitoringConfig.java @@ -0,0 +1,21 @@ +package org.baeldung.monitor.jmx; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.codahale.metrics.JmxReporter; +import com.codahale.metrics.MetricRegistry; + +@Configuration +public class MonitoringConfig { + @Autowired + private MetricRegistry registry; + + @Bean + public JmxReporter jmxReporter() { + JmxReporter reporter = JmxReporter.forRegistry(registry).build(); + reporter.start(); + return reporter; + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/repository/GenericEntityRepository.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/repository/GenericEntityRepository.java new file mode 100644 index 000000000000..7bb1e6dcdcd0 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/repository/GenericEntityRepository.java @@ -0,0 +1,7 @@ +package org.baeldung.repository; + +import org.baeldung.domain.GenericEntity; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface GenericEntityRepository extends JpaRepository { +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/service/LoginService.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/service/LoginService.java new file mode 100644 index 000000000000..34840fad6700 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/service/LoginService.java @@ -0,0 +1,5 @@ +package org.baeldung.service; + +public interface LoginService { + public boolean login(String userName, char[] password); +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/service/LoginServiceImpl.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/service/LoginServiceImpl.java new file mode 100644 index 000000000000..2e5ef89c4865 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/service/LoginServiceImpl.java @@ -0,0 +1,29 @@ +package org.baeldung.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.metrics.CounterService; +import org.springframework.stereotype.Service; + +@Service +public class LoginServiceImpl implements LoginService { + + private CounterService counterService; + + @Autowired + public LoginServiceImpl(CounterService counterService) { + this.counterService = counterService; + } + + public boolean login(String userName, char[] password) { + boolean success; + if (userName.equals("admin") && "secret".toCharArray().equals(password)) { + counterService.increment("counter.login.success"); + success = true; + } else { + counterService.increment("counter.login.failure"); + success = false; + } + return success; + } + +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/session/exception/Application.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/session/exception/Application.java new file mode 100644 index 000000000000..23d741b98c5a --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/session/exception/Application.java @@ -0,0 +1,23 @@ +package org.baeldung.session.exception; + +import org.baeldung.boot.model.Foo; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.context.annotation.Bean; +import org.springframework.orm.jpa.vendor.HibernateJpaSessionFactoryBean; + +@EntityScan(basePackageClasses = Foo.class) +@SpringBootApplication +public class Application { + public static void main(String[] args) { + System.setProperty("spring.config.name", "exception"); + System.setProperty("spring.profiles.active", "exception"); + SpringApplication.run(Application.class, args); + } + + @Bean + public HibernateJpaSessionFactoryBean sessionFactory() { + return new HibernateJpaSessionFactoryBean(); + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/session/exception/repository/FooRepository.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/session/exception/repository/FooRepository.java new file mode 100644 index 000000000000..679d691b26c8 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/session/exception/repository/FooRepository.java @@ -0,0 +1,10 @@ +package org.baeldung.session.exception.repository; + +import org.baeldung.boot.model.Foo; + +public interface FooRepository { + + void save(Foo foo); + + Foo get(Integer id); +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/session/exception/repository/FooRepositoryImpl.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/session/exception/repository/FooRepositoryImpl.java new file mode 100644 index 000000000000..83de888e5e1d --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/session/exception/repository/FooRepositoryImpl.java @@ -0,0 +1,25 @@ +package org.baeldung.session.exception.repository; + +import org.baeldung.boot.model.Foo; +import org.hibernate.SessionFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Repository; + +@Profile("exception") +@Repository +public class FooRepositoryImpl implements FooRepository { + @Autowired + private SessionFactory sessionFactory; + + @Override + public void save(Foo foo) { + sessionFactory.getCurrentSession().saveOrUpdate(foo); + } + + @Override + public Foo get(Integer id) { + return sessionFactory.getCurrentSession().get(Foo.class, id); + } + +} \ No newline at end of file diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/web/resolver/HeaderVersionArgumentResolver.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/web/resolver/HeaderVersionArgumentResolver.java new file mode 100644 index 000000000000..89a77f38d123 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/web/resolver/HeaderVersionArgumentResolver.java @@ -0,0 +1,26 @@ +package org.baeldung.web.resolver; + +import org.springframework.core.MethodParameter; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +import javax.servlet.http.HttpServletRequest; + +@Component +public class HeaderVersionArgumentResolver implements HandlerMethodArgumentResolver { + + @Override + public boolean supportsParameter(final MethodParameter methodParameter) { + return methodParameter.getParameterAnnotation(Version.class) != null; + } + + @Override + public Object resolveArgument(final MethodParameter methodParameter, final ModelAndViewContainer modelAndViewContainer, final NativeWebRequest nativeWebRequest, final WebDataBinderFactory webDataBinderFactory) throws Exception { + HttpServletRequest request = (HttpServletRequest) nativeWebRequest.getNativeRequest(); + + return request.getHeader("Version"); + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/web/resolver/Version.java b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/web/resolver/Version.java new file mode 100644 index 000000000000..2a9e6e60b320 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/java/org/baeldung/web/resolver/Version.java @@ -0,0 +1,11 @@ +package org.baeldung.web.resolver; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface Version { +} diff --git a/spring-custom-aop/spring-custom-aop/src/main/resources/application.properties b/spring-custom-aop/spring-custom-aop/src/main/resources/application.properties new file mode 100644 index 000000000000..72ed8795c979 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/resources/application.properties @@ -0,0 +1,43 @@ +server.port=8080 +server.contextPath=/springbootapp +management.port=8081 +management.address=127.0.0.1 + +endpoints.shutdown.enabled=true + +endpoints.jmx.domain=Spring Sample Application +endpoints.jmx.uniqueNames=true + +##jolokia.config.debug=true +##endpoints.jolokia.enabled=true +##endpoints.jolokia.path=jolokia + +spring.jmx.enabled=true +endpoints.jmx.enabled=true + +## for pretty printing of json when endpoints accessed over HTTP +http.mappers.jsonPrettyPrint=true + +## Configuring info endpoint +info.app.name=Spring Sample Application +info.app.description=This is my first spring boot application G1 +info.app.version=1.0.0 + +## Spring Security Configurations +security.user.name=admin1 +security.user.password=secret1 +management.security.role=SUPERUSER + +logging.level.org.springframework=INFO + +#Servlet Configuration +servlet.name=dispatcherExample +servlet.mapping=/dispatcherExampleURL + +#banner.charset=UTF-8 +#banner.location=classpath:banner.txt +#banner.image.location=classpath:banner.gif +#banner.image.width= //TODO +#banner.image.height= //TODO +#banner.image.margin= //TODO +#banner.image.invert= //TODO \ No newline at end of file diff --git a/spring-custom-aop/spring-custom-aop/src/main/resources/banner.txt b/spring-custom-aop/spring-custom-aop/src/main/resources/banner.txt new file mode 100644 index 000000000000..abfa666eb66f --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/resources/banner.txt @@ -0,0 +1,14 @@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@@#@@@@@########@@@@@@@@@@@@@@@@@@@@@@@@...@@@@@@@@@:..@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@#. @@@@@* *@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@. @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@#o @@@@@* @@@@@* @@@:*.*@@@@@@@: *8@@@ @@@@&:.#@. @o**@@@@**:@o*o@@:.:@@@@@:.o#@&*:@@@@ +@@@@@@@@@@@@* @@@@@* 8888 8@ @@@8 #@o 8@# .@ @@* :. @* @@@@ @. : &@ ** .@@@@ +@@@@@@@@@@. @ o@@@@@* *@@@o::& .* 8@@@@. @@ 8@@@@. @* @@@@ @. @@@& * @@@@# .@@@@ +@@@@@@@@@& @ @@@@@@* @@@@@@ 8 @@@@ .. o&&&&&&& @@ #@@@@. @* @@@@ @. @@@# * @@@@@ .@@@@ +@@@@@@@@@ @@o @@@@@@@* oooo* 8 @@@& @* @@@ # 88. 88. *& o#: @. @@@# *@ &#& .@@@@ +@@@@@@@@# @@@8 @@@@@@@* .*@@@#. *@@ @@@& :#@@@o .@@: *&@8 @o o@@: @. @@@# *@@#. :8# .@@@@ +@@@@@@@@@ @@@@ &@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@# o@@@@ @@@@@ +@@@@@& &@@@@ 8@@@@@@@@@8&8@@@@@#8#@@@o8@#&@@o&@@@&@@8@@&@@@@88@@8#@8&@@##@@@@@@#8@@#8@@88@@@@@ *@@@@@@@ +@@@# #@@@@#. @@@@@@@@@@@@@8@@8#o@&#@@@@o.@o*@@*.@@@.@&:8o8*@@@8&@@#@@@8@@@@8@#@@@8&@@@@@@#@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ \ No newline at end of file diff --git a/spring-custom-aop/spring-custom-aop/src/main/resources/custom.properties b/spring-custom-aop/spring-custom-aop/src/main/resources/custom.properties new file mode 100644 index 000000000000..34f31bcd50a2 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/resources/custom.properties @@ -0,0 +1,4 @@ +dispatcher.servlet.name=dispatcherExample +dispatcher.servlet.mapping=/dispatcherExampleURL +example.servlet.name=dispatcherExample +example.servlet.mapping=/dispatcherExampleURL \ No newline at end of file diff --git a/spring-custom-aop/spring-custom-aop/src/main/resources/demo.properties b/spring-custom-aop/spring-custom-aop/src/main/resources/demo.properties new file mode 100644 index 000000000000..649b64f59b3a --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/resources/demo.properties @@ -0,0 +1,6 @@ +spring.output.ansi.enabled=never +server.port=7070 + +# Security +security.user.name=admin +security.user.password=password \ No newline at end of file diff --git a/spring-custom-aop/spring-custom-aop/src/main/resources/logback.xml b/spring-custom-aop/spring-custom-aop/src/main/resources/logback.xml new file mode 100644 index 000000000000..ec0dc2469ae0 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/resources/logback.xml @@ -0,0 +1,19 @@ + + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-custom-aop/spring-custom-aop/src/main/resources/messages.properties b/spring-custom-aop/spring-custom-aop/src/main/resources/messages.properties new file mode 100644 index 000000000000..e4dbc44c3f5e --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/resources/messages.properties @@ -0,0 +1,4 @@ +greeting=Hello! Welcome to our website! +lang.change=Change the language +lang.eng=English +lang.fr=French \ No newline at end of file diff --git a/spring-custom-aop/spring-custom-aop/src/main/resources/messages_fr.properties b/spring-custom-aop/spring-custom-aop/src/main/resources/messages_fr.properties new file mode 100644 index 000000000000..ac5853717d5d --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/resources/messages_fr.properties @@ -0,0 +1,4 @@ +greeting=Bonjour! Bienvenue sur notre site! +lang.change=Changez la langue +lang.eng=Anglais +lang.fr=Francais \ No newline at end of file diff --git a/spring-custom-aop/spring-custom-aop/src/main/resources/public/error/404.html b/spring-custom-aop/spring-custom-aop/src/main/resources/public/error/404.html new file mode 100644 index 000000000000..df83ce219bfe --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/resources/public/error/404.html @@ -0,0 +1,8 @@ + + + RESOURCE NOT FOUND + + +

404 RESOURCE NOT FOUND

+ + \ No newline at end of file diff --git a/spring-custom-aop/spring-custom-aop/src/main/resources/templates/index.html b/spring-custom-aop/spring-custom-aop/src/main/resources/templates/index.html new file mode 100644 index 000000000000..2c4387ed108d --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/resources/templates/index.html @@ -0,0 +1,19 @@ + + + WebJars Demo + + + + +

+
+ × + Success! It is working as we expected. +
+
+ + + + + + diff --git a/spring-custom-aop/spring-custom-aop/src/main/resources/templates/international.html b/spring-custom-aop/spring-custom-aop/src/main/resources/templates/international.html new file mode 100644 index 000000000000..a2a5fbb5910e --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/resources/templates/international.html @@ -0,0 +1,29 @@ + + + + +Home + + + + +

+ +

+: + + + \ No newline at end of file diff --git a/spring-custom-aop/spring-custom-aop/src/main/resources/templates/other.html b/spring-custom-aop/spring-custom-aop/src/main/resources/templates/other.html new file mode 100644 index 000000000000..d13373f9fece --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/resources/templates/other.html @@ -0,0 +1,16 @@ + + + + +Spring Utils Demo + + + + Parameter set by you:

+ + \ No newline at end of file diff --git a/spring-custom-aop/spring-custom-aop/src/main/resources/templates/utils.html b/spring-custom-aop/spring-custom-aop/src/main/resources/templates/utils.html new file mode 100644 index 000000000000..93030f424fbd --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/resources/templates/utils.html @@ -0,0 +1,23 @@ + + + + +Spring Utils Demo + + + +

+

Set Parameter:

+

+ + +

+
+Another Page + + \ No newline at end of file diff --git a/spring-custom-aop/spring-custom-aop/src/main/webapp/WEB-INF/context.xml b/spring-custom-aop/spring-custom-aop/src/main/webapp/WEB-INF/context.xml new file mode 100644 index 000000000000..263bed443016 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/webapp/WEB-INF/context.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/spring-custom-aop/spring-custom-aop/src/main/webapp/WEB-INF/dispatcher.xml b/spring-custom-aop/spring-custom-aop/src/main/webapp/WEB-INF/dispatcher.xml new file mode 100644 index 000000000000..ade8e66777bf --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/webapp/WEB-INF/dispatcher.xml @@ -0,0 +1,16 @@ + + + + + + + + + \ No newline at end of file diff --git a/spring-custom-aop/spring-custom-aop/src/main/webapp/WEB-INF/web.xml b/spring-custom-aop/spring-custom-aop/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000000..60a4b079de3b --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,40 @@ + + + JSP + + index.html + index.htm + index.jsp + + + + + EEWebXmlServlet + com.baeldung.servlets.javaee.EEWebXmlServlet + + + + EEWebXmlServlet + /eewebxmlservlet + + + + + SpringBootWebXmlServlet + org.springframework.web.servlet.DispatcherServlet + + contextConfigLocation + /WEB-INF/dispatcher.xml + + 1 + + + + SpringBootWebXmlServlet + / + + + + diff --git a/spring-custom-aop/spring-custom-aop/src/main/webapp/annotationservlet.jsp b/spring-custom-aop/spring-custom-aop/src/main/webapp/annotationservlet.jsp new file mode 100644 index 000000000000..f21748df50e5 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/webapp/annotationservlet.jsp @@ -0,0 +1 @@ +

Annotation Servlet!

\ No newline at end of file diff --git a/spring-custom-aop/spring-custom-aop/src/main/webapp/index.jsp b/spring-custom-aop/spring-custom-aop/src/main/webapp/index.jsp new file mode 100644 index 000000000000..e53428277781 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/main/webapp/index.jsp @@ -0,0 +1 @@ +

Hello!

\ No newline at end of file diff --git a/spring-custom-aop/spring-custom-aop/src/test/java/com/baeldung/annotation/servletcomponentscan/SpringBootWithServletComponentIntegrationTest.java b/spring-custom-aop/spring-custom-aop/src/test/java/com/baeldung/annotation/servletcomponentscan/SpringBootWithServletComponentIntegrationTest.java new file mode 100644 index 000000000000..8d5eb56bf430 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/test/java/com/baeldung/annotation/servletcomponentscan/SpringBootWithServletComponentIntegrationTest.java @@ -0,0 +1,65 @@ +package com.baeldung.annotation.servletcomponentscan; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +import javax.servlet.FilterRegistration; +import javax.servlet.ServletContext; + +import static org.junit.Assert.*; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SpringBootAnnotatedApp.class) +@AutoConfigureMockMvc +@TestPropertySource(properties = { "security.basic.enabled=false" }) +public class SpringBootWithServletComponentIntegrationTest { + + @Autowired private ServletContext servletContext; + + @Test + public void givenServletContext_whenAccessAttrs_thenFoundAttrsPutInServletListner() { + assertNotNull(servletContext); + assertNotNull(servletContext.getAttribute("servlet-context-attr")); + assertEquals("test", servletContext.getAttribute("servlet-context-attr")); + } + + @Test + public void givenServletContext_whenCheckHelloFilterMappings_thenCorrect() { + assertNotNull(servletContext); + FilterRegistration filterRegistration = servletContext.getFilterRegistration("hello filter"); + + assertNotNull(filterRegistration); + assertTrue(filterRegistration + .getServletNameMappings() + .contains("echo servlet")); + } + + @Autowired private TestRestTemplate restTemplate; + + @Test + public void givenServletFilter_whenGetHello_thenRequestFiltered() { + ResponseEntity responseEntity = this.restTemplate.getForEntity("/hello", String.class); + assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); + assertEquals("filtering hello", responseEntity.getBody()); + } + + @Test + public void givenFilterAndServlet_whenPostEcho_thenEchoFiltered() { + ResponseEntity responseEntity = this.restTemplate.postForEntity("/echo", "echo", String.class); + assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); + assertEquals("filtering echo", responseEntity.getBody()); + } + + + +} + + diff --git a/spring-custom-aop/spring-custom-aop/src/test/java/com/baeldung/annotation/servletcomponentscan/SpringBootWithoutServletComponentIntegrationTest.java b/spring-custom-aop/spring-custom-aop/src/test/java/com/baeldung/annotation/servletcomponentscan/SpringBootWithoutServletComponentIntegrationTest.java new file mode 100644 index 000000000000..64507ad02c7d --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/test/java/com/baeldung/annotation/servletcomponentscan/SpringBootWithoutServletComponentIntegrationTest.java @@ -0,0 +1,50 @@ +package com.baeldung.annotation.servletcomponentscan; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +import javax.servlet.FilterRegistration; +import javax.servlet.ServletContext; + +import static org.junit.Assert.*; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SpringBootPlainApp.class) +@AutoConfigureMockMvc +@TestPropertySource(properties = { "security.basic.enabled=false" }) +public class SpringBootWithoutServletComponentIntegrationTest { + + @Autowired private ServletContext servletContext; + + @Autowired private TestRestTemplate restTemplate; + + @Test + public void givenServletContext_whenAccessAttrs_thenNotFound() { + assertNull(servletContext.getAttribute("servlet-context-attr")); + } + + @Test + public void givenServletFilter_whenGetHello_thenEndpointNotFound() { + ResponseEntity responseEntity = this.restTemplate.getForEntity("/hello", String.class); + assertEquals(HttpStatus.NOT_FOUND, responseEntity.getStatusCode()); + } + + @Test + public void givenServletContext_whenCheckFilterMappings_thenEmpty() { + assertNotNull(servletContext); + FilterRegistration filterRegistration = servletContext.getFilterRegistration("hello filter"); + + assertNull(filterRegistration); + } + +} + + diff --git a/spring-custom-aop/spring-custom-aop/src/test/java/com/baeldung/git/CommitIdIntegrationTest.java b/spring-custom-aop/spring-custom-aop/src/test/java/com/baeldung/git/CommitIdIntegrationTest.java new file mode 100644 index 000000000000..348d594c05a3 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/test/java/com/baeldung/git/CommitIdIntegrationTest.java @@ -0,0 +1,41 @@ +package com.baeldung.git; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(SpringRunner.class) +@ContextConfiguration(classes = CommitIdApplication.class) +public class CommitIdIntegrationTest { + + private static final Logger LOG = LoggerFactory.getLogger(CommitIdIntegrationTest.class); + + @Value("${git.commit.message.short:UNKNOWN}") + private String commitMessage; + + @Value("${git.branch:UNKNOWN}") + private String branch; + + @Value("${git.commit.id:UNKNOWN}") + private String commitId; + + @Test + public void whenInjecting_shouldDisplay() throws Exception { + + LOG.info(commitId); + LOG.info(commitMessage); + LOG.info(branch); + + assertThat(commitMessage).isNotEqualTo("UNKNOWN"); + + assertThat(branch).isNotEqualTo("UNKNOWN"); + + assertThat(commitId).isNotEqualTo("UNKNOWN"); + } +} \ No newline at end of file diff --git a/spring-custom-aop/spring-custom-aop/src/test/java/com/baeldung/intro/AppLiveTest.java b/spring-custom-aop/spring-custom-aop/src/test/java/com/baeldung/intro/AppLiveTest.java new file mode 100644 index 000000000000..af46fe04231d --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/test/java/com/baeldung/intro/AppLiveTest.java @@ -0,0 +1,41 @@ +package com.baeldung.intro; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import static org.hamcrest.Matchers.equalTo; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@RunWith(SpringRunner.class) +@SpringBootTest +@AutoConfigureMockMvc +@TestPropertySource(properties = { "security.basic.enabled=false" }) +public class AppLiveTest { + + @Autowired + private MockMvc mvc; + + @Test + public void getIndex() throws Exception { + mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().string(equalTo("Index Page"))); + } + + @Test + public void getLocal() throws Exception { + mvc.perform(MockMvcRequestBuilders.get("/local").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().string(equalTo("/local"))); + } + +} \ No newline at end of file diff --git a/spring-custom-aop/spring-custom-aop/src/test/java/com/baeldung/utils/UtilsControllerTest.java b/spring-custom-aop/spring-custom-aop/src/test/java/com/baeldung/utils/UtilsControllerTest.java new file mode 100644 index 000000000000..7aed45dbb018 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/test/java/com/baeldung/utils/UtilsControllerTest.java @@ -0,0 +1,41 @@ +package com.baeldung.utils; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.MockitoAnnotations; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + + +import com.baeldung.utils.controller.UtilsController; + +public class UtilsControllerTest { + + @InjectMocks + private UtilsController utilsController; + + private MockMvc mockMvc; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + this.mockMvc = MockMvcBuilders.standaloneSetup(utilsController) + .build(); + + } + + @Test + public void givenParameter_setRequestParam_andSetSessionAttribute() throws Exception { + String param = "testparam"; + this.mockMvc.perform( + post("/setParam") + .param("param", param) + .sessionAttr("parameter", param)) + .andExpect(status().isOk()); + } + +} diff --git a/spring-custom-aop/spring-custom-aop/src/test/java/com/baeldung/webjar/WebjarsdemoApplicationIntegrationTest.java b/spring-custom-aop/spring-custom-aop/src/test/java/com/baeldung/webjar/WebjarsdemoApplicationIntegrationTest.java new file mode 100644 index 000000000000..d6e71dcf6b7e --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/test/java/com/baeldung/webjar/WebjarsdemoApplicationIntegrationTest.java @@ -0,0 +1,18 @@ +package com.baeldung.webjar; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest(classes = WebjarsdemoApplication.class) +@WebAppConfiguration +public class WebjarsdemoApplicationIntegrationTest { + + @Test + public void contextLoads() { + } + +} diff --git a/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/SpringBootApplicationIntegrationTest.java b/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/SpringBootApplicationIntegrationTest.java new file mode 100644 index 000000000000..87c59a4662e2 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/SpringBootApplicationIntegrationTest.java @@ -0,0 +1,66 @@ +package org.baeldung; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; + +import org.baeldung.domain.Modes; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +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.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +import java.nio.charset.Charset; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest(classes = Application.class) +@WebAppConfiguration +public class SpringBootApplicationIntegrationTest { + @Autowired + private WebApplicationContext webApplicationContext; + private MockMvc mockMvc; + + @Before + public void setupMockMvc() { + mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); + } + + @Test + public void givenRequestHasBeenMade_whenMeetsAllOfGivenConditions_thenCorrect() throws Exception { + MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(), MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8")); + + mockMvc.perform(MockMvcRequestBuilders.get("/entity/all")).andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.content().contentType(contentType)).andExpect(jsonPath("$", hasSize(4))); + } + + @Test + public void givenRequestHasBeenMade_whenMeetsFindByDateOfGivenConditions_thenCorrect() throws Exception { + MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(), MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8")); + + mockMvc.perform(MockMvcRequestBuilders.get("/entity/findbydate/{date}", "2011-12-03T10:15:30")).andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.content().contentType(contentType)) + .andExpect(jsonPath("$.id", equalTo(1))); + } + + @Test + public void givenRequestHasBeenMade_whenMeetsFindByModeOfGivenConditions_thenCorrect() throws Exception { + MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(), MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8")); + + mockMvc.perform(MockMvcRequestBuilders.get("/entity/findbymode/{mode}", Modes.ALPHA.name())).andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.content().contentType(contentType)).andExpect(jsonPath("$.id", equalTo(1))); + } + + @Test + public void givenRequestHasBeenMade_whenMeetsFindByVersionOfGivenConditions_thenCorrect() throws Exception { + MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(), MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8")); + + mockMvc.perform(MockMvcRequestBuilders.get("/entity/findbyversion").header("Version", "1.0.0")).andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.content().contentType(contentType)) + .andExpect(jsonPath("$.id", equalTo(1))); + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/SpringBootJPAIntegrationTest.java b/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/SpringBootJPAIntegrationTest.java new file mode 100644 index 000000000000..d4b19e6a1db0 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/SpringBootJPAIntegrationTest.java @@ -0,0 +1,27 @@ +package org.baeldung; + +import org.baeldung.domain.GenericEntity; +import org.baeldung.repository.GenericEntityRepository; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest(classes = Application.class) +public class SpringBootJPAIntegrationTest { + @Autowired + private GenericEntityRepository genericEntityRepository; + + @Test + public void givenGenericEntityRepository_whenSaveAndRetreiveEntity_thenOK() { + GenericEntity genericEntity = genericEntityRepository.save(new GenericEntity("test")); + GenericEntity foundedEntity = genericEntityRepository.findOne(genericEntity.getId()); + assertNotNull(foundedEntity); + assertEquals(genericEntity.getValue(), foundedEntity.getValue()); + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/SpringBootMailIntegrationTest.java b/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/SpringBootMailIntegrationTest.java new file mode 100644 index 000000000000..10e3d6d60b3e --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/SpringBootMailIntegrationTest.java @@ -0,0 +1,81 @@ +package org.baeldung; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.web.context.WebApplicationContext; +import org.subethamail.wiser.Wiser; +import org.subethamail.wiser.WiserMessage; + +import javax.mail.MessagingException; +import java.io.IOException; +import java.util.List; + +import static org.hamcrest.Matchers.hasSize; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest(classes = Application.class) +public class SpringBootMailIntegrationTest { + @Autowired + private JavaMailSender javaMailSender; + + private Wiser wiser; + + private String userTo = "user2@localhost"; + private String userFrom = "user1@localhost"; + private String subject = "Test subject"; + private String textMail = "Text subject mail"; + + @Before + public void setUp() throws Exception { + final int TEST_PORT = 8025; + wiser = new Wiser(TEST_PORT); + wiser.start(); + } + + @After + public void tearDown() throws Exception { + wiser.stop(); + } + + @Test + public void givenMail_whenSendAndReceived_thenCorrect() throws Exception { + SimpleMailMessage message = composeEmailMessage(); + javaMailSender.send(message); + List messages = wiser.getMessages(); + + assertThat(messages, hasSize(1)); + WiserMessage wiserMessage = messages.get(0); + assertEquals(userFrom, wiserMessage.getEnvelopeSender()); + assertEquals(userTo, wiserMessage.getEnvelopeReceiver()); + assertEquals(subject, getSubject(wiserMessage)); + assertEquals(textMail, getMessage(wiserMessage)); + } + + private String getMessage(WiserMessage wiserMessage) throws MessagingException, IOException { + return wiserMessage.getMimeMessage().getContent().toString().trim(); + } + + private String getSubject(WiserMessage wiserMessage) throws MessagingException { + return wiserMessage.getMimeMessage().getSubject(); + } + + private SimpleMailMessage composeEmailMessage() { + SimpleMailMessage mailMessage = new SimpleMailMessage(); + mailMessage.setTo(userTo); + mailMessage.setReplyTo(userFrom); + mailMessage.setFrom(userFrom); + mailMessage.setSubject(subject); + mailMessage.setText(textMail); + return mailMessage; + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/boot/ApplicationIntegrationTest.java b/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/boot/ApplicationIntegrationTest.java new file mode 100644 index 000000000000..57a8abc1ee6a --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/boot/ApplicationIntegrationTest.java @@ -0,0 +1,17 @@ +package org.baeldung.boot; + +import org.baeldung.session.exception.Application; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest(classes = Application.class) +@TestPropertySource("classpath:exception.properties") +public class ApplicationIntegrationTest { + @Test + public void contextLoads() { + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/boot/DemoApplicationIntegrationTest.java b/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/boot/DemoApplicationIntegrationTest.java new file mode 100644 index 000000000000..4fcea35b4a4b --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/boot/DemoApplicationIntegrationTest.java @@ -0,0 +1,17 @@ +package org.baeldung.boot; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest(classes = DemoApplication.class) +@WebAppConfiguration +public class DemoApplicationIntegrationTest { + + @Test + public void contextLoads() { + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/boot/FooComponentTests.java b/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/boot/FooComponentTests.java new file mode 100644 index 000000000000..72ccc0bfb891 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/boot/FooComponentTests.java @@ -0,0 +1,70 @@ +package org.baeldung.boot; + +import org.baeldung.boot.components.FooService; +import org.baeldung.boot.model.Foo; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.doReturn; + +@RunWith(SpringRunner.class) +@SpringBootTest( + classes = DemoApplication.class, + webEnvironment = WebEnvironment.RANDOM_PORT) +public class FooComponentTests { + + @Autowired + private TestRestTemplate testRestTemplate; + + @SpyBean + private FooService fooService; + + @Before + public void init() throws Exception { + Foo foo = new Foo(); + foo.setId(5); + foo.setName("MOCKED_FOO"); + + doReturn(foo).when(fooService).getFooWithId(anyInt()); + + // doCallRealMethod().when(fooComponent).getFooWithName(anyString()); + } + + @Test + public void givenInquiryingFooWithId_whenFooComponentIsMocked_thenAssertMockedResult() { + Map pathVariables = new HashMap<>(); + pathVariables.put("id", "1"); + ResponseEntity fooResponse = testRestTemplate.getForEntity("/{id}", Foo.class, pathVariables); + + assertNotNull(fooResponse); + assertEquals(HttpStatus.OK, fooResponse.getStatusCode()); + assertEquals(5, fooResponse.getBody().getId().longValue()); + assertEquals("MOCKED_FOO", fooResponse.getBody().getName()); + } + + @Test + public void givenInquiryingFooWithName_whenFooComponentIsMocked_thenAssertMockedResult() { + Map pathVariables = new HashMap<>(); + pathVariables.put("name", "Foo_Name"); + ResponseEntity fooResponse = testRestTemplate.getForEntity("/?name={name}", Foo.class, pathVariables); + + assertNotNull(fooResponse); + assertEquals(HttpStatus.OK, fooResponse.getStatusCode()); + assertEquals(1, fooResponse.getBody().getId().longValue()); + } +} \ No newline at end of file diff --git a/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/boot/FooIntegrationTest.java b/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/boot/FooIntegrationTest.java new file mode 100644 index 000000000000..932cce26d5b3 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/boot/FooIntegrationTest.java @@ -0,0 +1,43 @@ +package org.baeldung.boot; +import java.util.HashMap; +import java.util.Map; + +import org.baeldung.boot.DemoApplication; +import org.baeldung.boot.model.Foo; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes=DemoApplication.class,webEnvironment = WebEnvironment.RANDOM_PORT) +public class FooIntegrationTest { + + @Autowired + private TestRestTemplate testRestTemplate; + + + @Test + public void givenInquiryingFooWithId_whenIdIsValid_thenHttpStatusOK(){ + Map pathVariables = new HashMap(); + pathVariables.put("id", "1"); + ResponseEntity fooResponse = testRestTemplate.getForEntity("/{id}", Foo.class, pathVariables); + Assert.assertNotNull(fooResponse); + Assert.assertEquals(HttpStatus.OK,fooResponse.getStatusCode()); + } + + @Test + public void givenInquiryingFooWithName_whenNameIsValid_thenHttpStatusOK(){ + Map pathVariables = new HashMap(); + pathVariables.put("name", "Foo_Name"); + ResponseEntity fooResponse = testRestTemplate.getForEntity("/?name={name}", Foo.class, pathVariables); + Assert.assertNotNull(fooResponse); + Assert.assertEquals(HttpStatus.OK,fooResponse.getStatusCode()); + } +} \ No newline at end of file diff --git a/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/boot/FooJPATest.java b/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/boot/FooJPATest.java new file mode 100644 index 000000000000..c29aa64e6c63 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/boot/FooJPATest.java @@ -0,0 +1,34 @@ +package org.baeldung.boot; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.baeldung.boot.model.Foo; +import org.baeldung.boot.repository.FooRepository; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@DataJpaTest +public class FooJPATest { + + @Autowired + private TestEntityManager entityManager; + + @Autowired + private FooRepository repository; + + @Test + public void findFooByName() { + this.entityManager.persist(new Foo("Foo_Name_2")); + Foo foo = this.repository.findByName("Foo_Name_2"); + assertNotNull(foo); + assertEquals("Foo_Name_2",foo.getName()); + // Due to having Insert query for Foo with Id 1, so TestEntityManager generates new Id of 2 + assertEquals(2l,foo.getId().longValue()); + } +} \ No newline at end of file diff --git a/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/boot/FooJsonTest.java b/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/boot/FooJsonTest.java new file mode 100644 index 000000000000..2789ed0a8c15 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/boot/FooJsonTest.java @@ -0,0 +1,35 @@ +package org.baeldung.boot; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.baeldung.boot.model.Foo; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.json.JsonTest; +import org.springframework.boot.test.json.JacksonTester; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@JsonTest +public class FooJsonTest { + + @Autowired + private JacksonTester json; + + + @Test + public void testSerialize() throws Exception { + Foo foo = new Foo(3, "Foo_Name_3"); + assertThat(this.json.write(foo)).isEqualToJson("expected.json"); + assertThat(this.json.write(foo)).hasJsonPathStringValue("@.name"); + assertThat(this.json.write(foo)).extractingJsonPathStringValue("@.name").isEqualTo("Foo_Name_3"); + } + + @Test + public void testDeserialize() throws Exception { + String content = "{\"id\":4,\"name\":\"Foo_Name_4\"}"; + assertThat(this.json.parseObject(content).getName()).isEqualTo("Foo_Name_4"); + assertThat(this.json.parseObject(content).getId()==4); + } +} \ No newline at end of file diff --git a/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/boot/repository/FooRepositoryIntegrationTest.java b/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/boot/repository/FooRepositoryIntegrationTest.java new file mode 100644 index 000000000000..a844b26b2d12 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/boot/repository/FooRepositoryIntegrationTest.java @@ -0,0 +1,34 @@ +package org.baeldung.boot.repository; + +import static org.junit.Assert.assertThat; + +import org.baeldung.boot.DemoApplicationIntegrationTest; +import org.baeldung.boot.model.Foo; + +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.is; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class FooRepositoryIntegrationTest extends DemoApplicationIntegrationTest { + @Autowired + private FooRepository fooRepository; + + @Before + public void setUp() { + fooRepository.save(new Foo("Foo")); + fooRepository.save(new Foo("Bar")); + } + + @Test + public void testFindByName() { + Foo foo = fooRepository.findByName("Bar"); + assertThat(foo, notNullValue()); + assertThat(foo.getId(), is(2)); + } + +} diff --git a/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/boot/repository/HibernateSessionIntegrationTest.java b/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/boot/repository/HibernateSessionIntegrationTest.java new file mode 100644 index 000000000000..be992bcc364b --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/boot/repository/HibernateSessionIntegrationTest.java @@ -0,0 +1,32 @@ +package org.baeldung.boot.repository; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.junit.Assert.assertThat; + +import org.baeldung.boot.ApplicationIntegrationTest; +import org.baeldung.boot.model.Foo; +import org.baeldung.session.exception.repository.FooRepository; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.TestPropertySource; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +@TestPropertySource("classpath:exception-hibernate.properties") +public class HibernateSessionIntegrationTest extends ApplicationIntegrationTest { + @Autowired + private FooRepository fooRepository; + + @Test + public void whenSavingWithCurrentSession_thenThrowNoException() { + Foo foo = new Foo("Exception Solved"); + fooRepository.save(foo); + foo = null; + foo = fooRepository.get(1); + + assertThat(foo, notNullValue()); + assertThat(foo.getId(), is(1)); + assertThat(foo.getName(), is("Exception Solved")); + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/boot/repository/NoHibernateSessionIntegrationTest.java b/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/boot/repository/NoHibernateSessionIntegrationTest.java new file mode 100644 index 000000000000..55b7fa7216ee --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/boot/repository/NoHibernateSessionIntegrationTest.java @@ -0,0 +1,21 @@ +package org.baeldung.boot.repository; + +import org.baeldung.boot.ApplicationIntegrationTest; +import org.baeldung.boot.model.Foo; +import org.baeldung.session.exception.repository.FooRepository; +import org.hibernate.HibernateException; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class NoHibernateSessionIntegrationTest extends ApplicationIntegrationTest { + @Autowired + private FooRepository fooRepository; + + @Test(expected = HibernateException.class) + public void whenSavingWithoutCurrentSession_thenThrowException() { + Foo foo = new Foo("Exception Thrown"); + fooRepository.save(foo); + } +} diff --git a/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/client/DetailsServiceClientIntegrationTest.java b/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/client/DetailsServiceClientIntegrationTest.java new file mode 100644 index 000000000000..5627855aa3d6 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/test/java/org/baeldung/client/DetailsServiceClientIntegrationTest.java @@ -0,0 +1,46 @@ +package org.baeldung.client; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.client.RestClientTest; +import org.springframework.http.MediaType; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.client.MockRestServiceServer; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; + +@RunWith(SpringRunner.class) +@RestClientTest(DetailsServiceClient.class) +public class DetailsServiceClientIntegrationTest { + + @Autowired + private DetailsServiceClient client; + + @Autowired + private MockRestServiceServer server; + + @Autowired + private ObjectMapper objectMapper; + + @Before + public void setUp() throws Exception { + String detailsString = objectMapper.writeValueAsString(new Details("John Smith", "john")); + this.server.expect(requestTo("/john/details")).andRespond(withSuccess(detailsString, MediaType.APPLICATION_JSON)); + } + + @Test + public void whenCallingGetUserDetails_thenClientExecutesCorrectCall() throws Exception { + + Details details = this.client.getUserDetails("john"); + + assertThat(details.getLogin()).isEqualTo("john"); + assertThat(details.getName()).isEqualTo("John Smith"); + + } + +} diff --git a/spring-custom-aop/spring-custom-aop/src/test/resources/application.properties b/spring-custom-aop/spring-custom-aop/src/test/resources/application.properties new file mode 100644 index 000000000000..0e6cb86bc57f --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/test/resources/application.properties @@ -0,0 +1,5 @@ +spring.mail.host=localhost +spring.mail.port=8025 +spring.mail.properties.mail.smtp.auth=false + +security.basic.enabled=false \ No newline at end of file diff --git a/spring-custom-aop/spring-custom-aop/src/test/resources/exception-hibernate.properties b/spring-custom-aop/spring-custom-aop/src/test/resources/exception-hibernate.properties new file mode 100644 index 000000000000..cde746acb931 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/test/resources/exception-hibernate.properties @@ -0,0 +1,2 @@ +spring.profiles.active=exception +spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate4.SpringSessionContext diff --git a/spring-custom-aop/spring-custom-aop/src/test/resources/exception.properties b/spring-custom-aop/spring-custom-aop/src/test/resources/exception.properties new file mode 100644 index 000000000000..c55e415a3ae8 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/test/resources/exception.properties @@ -0,0 +1,6 @@ +# Security +security.user.name=admin +security.user.password=password + +spring.dao.exceptiontranslation.enabled=false +spring.profiles.active=exception \ No newline at end of file diff --git a/spring-custom-aop/spring-custom-aop/src/test/resources/import.sql b/spring-custom-aop/spring-custom-aop/src/test/resources/import.sql new file mode 100644 index 000000000000..a38241027187 --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/test/resources/import.sql @@ -0,0 +1 @@ +Insert into Foo values(1,'Foo_Name'); \ No newline at end of file diff --git a/spring-custom-aop/spring-custom-aop/src/test/resources/org/baeldung/boot/expected.json b/spring-custom-aop/spring-custom-aop/src/test/resources/org/baeldung/boot/expected.json new file mode 100644 index 000000000000..f5409421a62d --- /dev/null +++ b/spring-custom-aop/spring-custom-aop/src/test/resources/org/baeldung/boot/expected.json @@ -0,0 +1,4 @@ +{ + "id":3, + "name":"Foo_Name_3" +} \ No newline at end of file diff --git a/spring-data-cassandra/pom.xml b/spring-data-cassandra/pom.xml index ef664fef7923..49a222e93d84 100644 --- a/spring-data-cassandra/pom.xml +++ b/spring-data-cassandra/pom.xml @@ -37,6 +37,12 @@ org.springframework spring-core ${org.springframework.version} + + + commons-logging + commons-logging + + junit @@ -72,6 +78,12 @@ org.hectorclient hector-core ${hector-core.version} + + + commons-logging + commons-logging + + com.datastax.cassandra @@ -94,11 +106,6 @@ jcl-over-slf4j ${org.slf4j.version} - - org.slf4j - log4j-over-slf4j - ${org.slf4j.version} - diff --git a/spring-data-cassandra/src/main/resources/logback.xml b/spring-data-cassandra/src/main/resources/logback.xml index 215eeede64ee..ec0dc2469ae0 100644 --- a/spring-data-cassandra/src/main/resources/logback.xml +++ b/spring-data-cassandra/src/main/resources/logback.xml @@ -1,5 +1,5 @@ + - web - %date [%thread] %-5level %logger{36} - %message%n @@ -16,5 +16,4 @@ - \ No newline at end of file diff --git a/spring-data-couchbase-2/pom.xml b/spring-data-couchbase-2/pom.xml index 9284234638a4..26a9ffcf2d4c 100644 --- a/spring-data-couchbase-2/pom.xml +++ b/spring-data-couchbase-2/pom.xml @@ -14,6 +14,12 @@ org.springframework spring-context ${spring-framework.version} + + + commons-logging + commons-logging + + org.springframework @@ -56,11 +62,6 @@ jcl-over-slf4j ${org.slf4j.version} - - org.slf4j - log4j-over-slf4j - ${org.slf4j.version} - diff --git a/spring-data-couchbase-2/src/main/resources/logback.xml b/spring-data-couchbase-2/src/main/resources/logback.xml index d9067fd1da73..ec0dc2469ae0 100644 --- a/spring-data-couchbase-2/src/main/resources/logback.xml +++ b/spring-data-couchbase-2/src/main/resources/logback.xml @@ -1,5 +1,5 @@ + - web - %date [%thread] %-5level %logger{36} - %message%n @@ -8,10 +8,12 @@ - + + + + - \ No newline at end of file diff --git a/spring-data-couchbase-2/src/test/resources/logback.xml b/spring-data-couchbase-2/src/test/resources/logback.xml index d9067fd1da73..ec0dc2469ae0 100644 --- a/spring-data-couchbase-2/src/test/resources/logback.xml +++ b/spring-data-couchbase-2/src/test/resources/logback.xml @@ -1,5 +1,5 @@ + - web - %date [%thread] %-5level %logger{36} - %message%n @@ -8,10 +8,12 @@ - + + + + - \ No newline at end of file diff --git a/spring-data-dynamodb/pom.xml b/spring-data-dynamodb/pom.xml index 8b06c0b36fe7..3216318665bb 100644 --- a/spring-data-dynamodb/pom.xml +++ b/spring-data-dynamodb/pom.xml @@ -94,6 +94,12 @@ com.amazonaws aws-java-sdk-dynamodb ${aws-java-sdk-dynamodb.version} + + + commons-logging + commons-logging + + com.github.derjust diff --git a/spring-data-dynamodb/src/main/resources/logback.xml b/spring-data-dynamodb/src/main/resources/logback.xml index c0bc60291012..ec0dc2469ae0 100644 --- a/spring-data-dynamodb/src/main/resources/logback.xml +++ b/spring-data-dynamodb/src/main/resources/logback.xml @@ -1,14 +1,19 @@ + - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - + + web - %date [%thread] %-5level %logger{36} - %message%n + + + + + + - - - + + - + + + + \ No newline at end of file diff --git a/spring-data-elasticsearch/pom.xml b/spring-data-elasticsearch/pom.xml index b38a6a10bc55..513a0a591bb6 100644 --- a/spring-data-elasticsearch/pom.xml +++ b/spring-data-elasticsearch/pom.xml @@ -30,6 +30,12 @@ org.springframework spring-core ${org.springframework.version} + + + commons-logging + commons-logging + + org.springframework.data @@ -71,11 +77,6 @@ jcl-over-slf4j ${org.slf4j.version} - - org.slf4j - log4j-over-slf4j - ${org.slf4j.version} - org.elasticsearch diff --git a/spring-data-elasticsearch/src/main/resources/logback.xml b/spring-data-elasticsearch/src/main/resources/logback.xml index db75fcbe4004..ec0dc2469ae0 100644 --- a/spring-data-elasticsearch/src/main/resources/logback.xml +++ b/spring-data-elasticsearch/src/main/resources/logback.xml @@ -1,20 +1,19 @@ + - - elasticsearch - %date [%thread] %-5level %logger{36} - %message%n + web - %date [%thread] %-5level %logger{36} - %message%n + - - - + + - \ No newline at end of file diff --git a/spring-data-gemfire/README.md b/spring-data-gemfire/README.md new file mode 100644 index 000000000000..78199e90d040 --- /dev/null +++ b/spring-data-gemfire/README.md @@ -0,0 +1,3 @@ +### Relevant articles + +- [A Guide to GemFire with Spring Data](http://www.baeldung.com/spring-data-gemfire) diff --git a/spring-data-gemfire/pom.xml b/spring-data-gemfire/pom.xml new file mode 100644 index 000000000000..ab510bc2bc69 --- /dev/null +++ b/spring-data-gemfire/pom.xml @@ -0,0 +1,109 @@ + + + 4.0.0 + + com.baeldung + spring-data-gemfire + 1.0.0-SNAPSHOT + jar + + 2.19.1 + 1.7.4.RELEASE + 7.0.1 + 1.0 + 4.12 + 1.3 + 4.3.0.RELEASE + 4.3.0.RELEASE + + + + + org.springframework.data + spring-data-gemfire + ${spring-data-gemfire-version} + + + + com.gemstone.gemfire + gemfire + ${gemfire-version} + + + + com.google.collections + google-collections + ${google-collections-version} + + + + junit + junit + ${junit-version} + test + + + org.hamcrest + hamcrest-core + + + + + org.hamcrest + hamcrest-library + ${hamcrest-library-version} + test + + + org.springframework + spring-context + ${spring-context-version} + + + commons-logging + commons-logging + + + + + org.springframework + spring-test + ${spring-test-version} + test + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + **/*IntegrationTest.java + **/*LiveTest.java + + + + + + + + + spring-snapshots + Spring Snapshots + http://repo.spring.io/libs-snapshot + + true + + + + gemstone + http://dist.gemstone.com.s3.amazonaws.com/maven/release/ + + + + \ No newline at end of file diff --git a/spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/function/FunctionExecution.java b/spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/function/FunctionExecution.java new file mode 100644 index 000000000000..a3ddf324145e --- /dev/null +++ b/spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/function/FunctionExecution.java @@ -0,0 +1,16 @@ +package com.baeldung.spring.data.gemfire.function; + +import org.springframework.context.annotation.ComponentScan; +import org.springframework.data.gemfire.function.annotation.FunctionId; +import org.springframework.data.gemfire.function.annotation.OnRegion; +import org.springframework.data.gemfire.function.annotation.RegionData; + +import java.util.Map; +import java.util.Set; + +@OnRegion(region="employee") +public interface FunctionExecution { + @FunctionId("greeting") + public void execute(String message); + +} diff --git a/spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/function/FunctionImpl.java b/spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/function/FunctionImpl.java new file mode 100644 index 000000000000..9fc6a2155d85 --- /dev/null +++ b/spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/function/FunctionImpl.java @@ -0,0 +1,18 @@ +package com.baeldung.spring.data.gemfire.function; + +import org.springframework.data.gemfire.function.annotation.GemfireFunction; +import org.springframework.stereotype.Component; + +@Component +public class FunctionImpl { + + @GemfireFunction + public void greeting(String message){ + System.out.println("Message "+message); + } + + @GemfireFunction + public String sayHello(String message){ + return "Hello "+message; + } +} diff --git a/spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/function/GemfireConfiguration.java b/spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/function/GemfireConfiguration.java new file mode 100644 index 000000000000..0049f82ddce9 --- /dev/null +++ b/spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/function/GemfireConfiguration.java @@ -0,0 +1,65 @@ +package com.baeldung.spring.data.gemfire.function; + +import com.baeldung.spring.data.gemfire.model.Employee; +import com.baeldung.spring.data.gemfire.repository.EmployeeRepository; +import com.gemstone.gemfire.cache.DataPolicy; +import com.gemstone.gemfire.cache.GemFireCache; +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.data.gemfire.CacheFactoryBean; +import org.springframework.data.gemfire.LocalRegionFactoryBean; +import org.springframework.data.gemfire.function.config.EnableGemfireFunctionExecutions; +import org.springframework.data.gemfire.function.config.EnableGemfireFunctions; +import org.springframework.data.gemfire.repository.config.EnableGemfireRepositories; +import java.util.Properties; + +@Configuration +@ComponentScan +@EnableGemfireRepositories(basePackages = "com.baeldung.spring.data.gemfire.repository") +@EnableGemfireFunctions +@EnableGemfireFunctionExecutions(basePackages = "com.baeldung.spring.data.gemfire.function") +public class GemfireConfiguration { + + @Autowired + EmployeeRepository employeeRepository; + + @Autowired + FunctionExecution functionExecution; + + + @Bean + Properties gemfireProperties() { + Properties gemfireProperties = new Properties(); + gemfireProperties.setProperty("name", "SpringDataGemFireApplication"); + gemfireProperties.setProperty("mcast-port", "0"); + gemfireProperties.setProperty("log-level", "config"); + return gemfireProperties; + } + + @Bean + @Autowired + CacheFactoryBean gemfireCache() { + CacheFactoryBean gemfireCache = new CacheFactoryBean(); + gemfireCache.setClose(true); + gemfireCache.setProperties(gemfireProperties()); + return gemfireCache; + } + + + @Bean(name="employee") + @Autowired + LocalRegionFactoryBean getEmployee(final GemFireCache cache) { + LocalRegionFactoryBean employeeRegion = new LocalRegionFactoryBean(); + employeeRegion.setCache(cache); + employeeRegion.setClose(false); + employeeRegion.setName("employee"); + employeeRegion.setPersistent(false); + employeeRegion.setDataPolicy(DataPolicy.PRELOADED); + return employeeRegion; + } + + +} + diff --git a/spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/model/Employee.java b/spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/model/Employee.java new file mode 100644 index 000000000000..9c29b28dca1f --- /dev/null +++ b/spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/model/Employee.java @@ -0,0 +1,41 @@ +package com.baeldung.spring.data.gemfire.model; + +import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.PersistenceConstructor; +import org.springframework.data.gemfire.mapping.Region; + + +@Region("employee") +public class Employee { + + @Id + public String name; + public double salary; + + @PersistenceConstructor + public Employee(String name, double salary) { + this.name = name; + this.salary = salary; + } + + @Override + public String toString() { + return name + " is " + salary + " years old."; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public double getSalary() { + return salary; + } + + public void setSalary(int salary) { + this.salary = salary; + } +} \ No newline at end of file diff --git a/spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/repository/EmployeeRepository.java b/spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/repository/EmployeeRepository.java new file mode 100644 index 000000000000..30799a2b99a1 --- /dev/null +++ b/spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/repository/EmployeeRepository.java @@ -0,0 +1,17 @@ +package com.baeldung.spring.data.gemfire.repository; + +import com.baeldung.spring.data.gemfire.model.Employee; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface EmployeeRepository extends CrudRepository { + + Employee findByName(String name); + + Iterable findBySalaryGreaterThan(double salary); + + Iterable findBySalaryLessThan(double salary); + + Iterable findBySalaryGreaterThanAndSalaryLessThan(double salary1, double salary2); +} diff --git a/spring-data-gemfire/src/main/resources/application-context.xml b/spring-data-gemfire/src/main/resources/application-context.xml new file mode 100644 index 000000000000..e618719b301a --- /dev/null +++ b/spring-data-gemfire/src/main/resources/application-context.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-data-gemfire/src/test/java/com/baeldung/spring/data/gemfire/repository/EmployeeRepositoryIntegrationTest.java b/spring-data-gemfire/src/test/java/com/baeldung/spring/data/gemfire/repository/EmployeeRepositoryIntegrationTest.java new file mode 100644 index 000000000000..26f7bc8a4ef5 --- /dev/null +++ b/spring-data-gemfire/src/test/java/com/baeldung/spring/data/gemfire/repository/EmployeeRepositoryIntegrationTest.java @@ -0,0 +1,98 @@ +package com.baeldung.spring.data.gemfire.repository; + +import com.baeldung.spring.data.gemfire.function.FunctionExecution; +import com.baeldung.spring.data.gemfire.function.GemfireConfiguration; +import com.baeldung.spring.data.gemfire.model.Employee; +import com.google.common.collect.Lists; +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.AnnotationConfigContextLoader; + +import java.util.List; + +import static junit.framework.Assert.assertEquals; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes=GemfireConfiguration.class, loader=AnnotationConfigContextLoader.class) +public class EmployeeRepositoryIntegrationTest { + + @Autowired private + EmployeeRepository employeeRepository; + + @Autowired + private FunctionExecution execution; + + @Test + public void whenEmployeeIsSaved_ThenAbleToRead(){ + Employee employee=new Employee("John Davidson",4550.00); + employeeRepository.save(employee); + + List employees= Lists.newArrayList(employeeRepository.findAll()); + + assertEquals(1, employees.size()); + } + + @Test + public void whenSalaryGreaterThan_ThenEmployeeFound(){ + + Employee employee=new Employee("John Davidson",4550.00); + Employee employee1=new Employee("Adam Davidson",3500.00); + Employee employee2=new Employee("Chris Davidson",5600.00); + + employeeRepository.save(employee); + employeeRepository.save(employee1); + employeeRepository.save(employee2); + + List employees= Lists.newArrayList(employeeRepository.findBySalaryGreaterThan(4000.00)); + + assertEquals(2,employees.size()); + + } + + @Test + public void whenSalaryLessThan_ThenEmployeeFound(){ + + Employee employee=new Employee("John Davidson",4550.00); + Employee employee1=new Employee("Adam Davidson",3500.00); + Employee employee2=new Employee("Chris Davidson",5600.00); + + employeeRepository.save(employee); + employeeRepository.save(employee1); + employeeRepository.save(employee2); + + List employees= Lists.newArrayList(employeeRepository.findBySalaryLessThan(4000)); + + assertEquals(1,employees.size()); + + } + @Test + public void whenSalaryBetween_ThenEmployeeFound(){ + + Employee employee=new Employee("John Davidson",4550.00); + Employee employee1=new Employee("Adam Davidson",3500.00); + Employee employee2=new Employee("Chris Davidson",5600.00); + + employeeRepository.save(employee); + employeeRepository.save(employee1); + employeeRepository.save(employee2); + + List employees= Lists.newArrayList(employeeRepository.findBySalaryGreaterThanAndSalaryLessThan(3500,5000)); + + assertEquals(1,employees.size()); + + } + + @Test + public void whenFunctionExecutedFromClient_ThenFunctionExecutedOnServer(){ + execution.execute("Hello World"); + } + + @After + public void cleanup(){ + employeeRepository.deleteAll(); + } +} diff --git a/spring-data-javaslang/.gitignore b/spring-data-javaslang/.gitignore new file mode 100644 index 000000000000..7ee5423d1475 --- /dev/null +++ b/spring-data-javaslang/.gitignore @@ -0,0 +1,5 @@ +/target/ +/project/ +.idea +.classpath +.eclipse diff --git a/spring-data-javaslang/pom.xml b/spring-data-javaslang/pom.xml new file mode 100644 index 000000000000..76fbce1e2e20 --- /dev/null +++ b/spring-data-javaslang/pom.xml @@ -0,0 +1,144 @@ + + + 4.0.0 + spring-data-javaslang + spring-data-javaslang + 0.0.1-SNAPSHOT + + UTF-8 + 2.19.1 + 3.1 + 2.9.1-01 + 2.3.7-01 + 2.4.8 + 2.0.5 + 4.12 + 1.5.1.RELEASE + 4.3.6.RELEASE + 4.3.6.RELEASE + 4.3.6.RELEASE + ${basedir}/src/test/java + + + org.springframework.boot + spring-boot-starter-parent + 1.5.1.RELEASE + + + + test-app + + verify + + + org.springframework.boot + spring-boot-maven-plugin + + + spring-boot-run + verify + + run + + false + + + + + + + + + + + maven-compiler-plugin + 3.5.1 + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + ${project.build.testSourceDirectory} + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + **/*IntegrationTest.java + **/*LiveTest.java + + + + + + + + + org.springframework.boot + spring-boot-devtools + true + + + + com.h2database + h2 + 1.4.193 + + + + io.javaslang + javaslang + ${javaslang.version} + + + + org.springframework.data + spring-data-jpa + 1.11.0.RELEASE + + + + org.springframework.boot + spring-boot + ${spring-boot.version} + + + + org.springframework.boot + spring-boot-starter-data-jpa + ${spring-boot.version} + + + + org.springframework.boot + spring-boot-starter-test + ${spring-boot.version} + + + + org.springframework + spring-context + ${spring-context.version} + + + + org.springframework + spring-core + ${spring-core.version} + + + + junit + junit + ${junit.version} + test + + + \ No newline at end of file diff --git a/spring-data-javaslang/src/main/java/com/baeldung/spring_data/model/Book.java b/spring-data-javaslang/src/main/java/com/baeldung/spring_data/model/Book.java new file mode 100644 index 000000000000..95653abb6c60 --- /dev/null +++ b/spring-data-javaslang/src/main/java/com/baeldung/spring_data/model/Book.java @@ -0,0 +1,45 @@ +package com.baeldung.spring_data.model; + +import javaslang.collection.Seq; + +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.CollectionTable; +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; + +@Entity +@Table(name = "book") +public class Book { + + @GeneratedValue + @Id + private Long id; + + private String title; + + private Seq authors; + + + public void setTitle(String title){ + this.title = title; + } + + public String getTitle(){ + return this.title; + } + + public Long getId(){ + return this.id; + } + + public void setAuthors(Seq authors){ + this.authors = authors; + } + + public Seq getAuthors(){ + return this.authors; + } +} diff --git a/spring-data-javaslang/src/main/java/com/baeldung/spring_data/model/JavaBook.java b/spring-data-javaslang/src/main/java/com/baeldung/spring_data/model/JavaBook.java new file mode 100644 index 000000000000..ab99b0d92909 --- /dev/null +++ b/spring-data-javaslang/src/main/java/com/baeldung/spring_data/model/JavaBook.java @@ -0,0 +1,41 @@ +package com.baeldung.spring_data.model; + +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; + +import java.util.List; + +@Entity +@Table(name = "java_book") +public class JavaBook { + + @GeneratedValue + @Id + private Long id; + + private String title; + + @ElementCollection + private List authors; + + + public void setAuthors(List authors){ + this.authors = authors; + } + + public void setTitle(String title){ + this.title = title; + } + + public String getTitle(){ + return this.title; + } + + public Long getId(){ + return this.id; + } +} + diff --git a/spring-data-javaslang/src/main/java/com/baeldung/spring_data/repository/BookRepository.java b/spring-data-javaslang/src/main/java/com/baeldung/spring_data/repository/BookRepository.java new file mode 100644 index 000000000000..75b6d0b426c8 --- /dev/null +++ b/spring-data-javaslang/src/main/java/com/baeldung/spring_data/repository/BookRepository.java @@ -0,0 +1,19 @@ +package com.baeldung.spring_data.repository; + +import com.baeldung.spring_data.model.Book; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import javaslang.collection.Seq; +import javaslang.control.Option; + +@Repository +public interface BookRepository extends JpaRepository{ + Book save(Book book); + + Option findById(Long id); + + Option> findByTitleContaining(String title); + +} diff --git a/spring-data-javaslang/src/main/java/com/baeldung/spring_data/repository/JavaBookRepository.java b/spring-data-javaslang/src/main/java/com/baeldung/spring_data/repository/JavaBookRepository.java new file mode 100644 index 000000000000..a4aeab85ee52 --- /dev/null +++ b/spring-data-javaslang/src/main/java/com/baeldung/spring_data/repository/JavaBookRepository.java @@ -0,0 +1,17 @@ +package com.baeldung.spring_data.repository; + +import com.baeldung.spring_data.model.JavaBook; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface JavaBookRepository extends JpaRepository{ + JavaBook save(JavaBook book); + + JavaBook findById(Long id); + + List findByTitleContaining(String title); +} \ No newline at end of file diff --git a/spring-data-javaslang/src/main/java/com/baeldung/spring_data_app/MainApp.java b/spring-data-javaslang/src/main/java/com/baeldung/spring_data_app/MainApp.java new file mode 100644 index 000000000000..d8a194e92e0b --- /dev/null +++ b/spring-data-javaslang/src/main/java/com/baeldung/spring_data_app/MainApp.java @@ -0,0 +1,21 @@ +package com.baeldung.spring_data_app; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +@Configuration +@EnableJpaRepositories("com.baeldung.spring_data.repository") +@EnableTransactionManagement +@EntityScan("com.baeldung.spring_data.model") +@SpringBootApplication +public class MainApp { + public static void main(String[] args){ + SpringApplication.run(MainApp.class, args); + } +} diff --git a/spring-data-javaslang/src/test/java/com/baeldung/spring_data_tests/SpringIntegrationTest.java b/spring-data-javaslang/src/test/java/com/baeldung/spring_data_tests/SpringIntegrationTest.java new file mode 100644 index 000000000000..7a23fa1ef2b2 --- /dev/null +++ b/spring-data-javaslang/src/test/java/com/baeldung/spring_data_tests/SpringIntegrationTest.java @@ -0,0 +1,89 @@ +package com.baeldung.spring_data_tests; + +import com.baeldung.spring_data.model.Book; +import com.baeldung.spring_data.model.JavaBook; +import com.baeldung.spring_data.repository.BookRepository; +import com.baeldung.spring_data.repository.JavaBookRepository; +import com.baeldung.spring_data_app.MainApp; +import javaslang.collection.List; +import javaslang.collection.Seq; +import javaslang.control.Option; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.ArrayList; + +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = MainApp.class,webEnvironment = WebEnvironment.NONE) +public class SpringIntegrationTest { + + @Autowired + private JavaBookRepository javaRepository; + + @Autowired + private BookRepository repository; + + @Test + public void should_return_seq(){ + Seq authors = List.of("author1","author2"); + Book testBook = new Book(); + testBook.setTitle("Javaslang in Spring Data Seq Test Return"); + testBook.setAuthors(authors); + Book book = repository.save(testBook); + Option> books = repository.findByTitleContaining("Seq Test"); + + assertThat(books).isNotEmpty(); + } + + + @Test + public void should_return_option_with_book(){ + Seq authors = List.of("author1","author2"); + Book testBook = new Book(); + testBook.setTitle("Javaslang in Spring Data"); + testBook.setAuthors(authors); + Book book = repository.save(testBook); + Option retBook = repository.findById(1L); + + assertThat(retBook.isDefined()).isTrue(); + assertThat(retBook).isNotEmpty(); + } + + @Test + public void should_return_list(){ + ArrayList authors = new ArrayList(); + authors.add("author1"); + authors.add("author2"); + JavaBook testBook = new JavaBook(); + testBook.setTitle("Javaslang in Spring Data Seq Return"); + testBook.setAuthors(authors); + JavaBook book = javaRepository.save(testBook); + java.util.List books = javaRepository.findByTitleContaining("Seq"); + assertThat(books) + .isNotEmpty() + .hasSize(1) + .extracting("title") + .contains("Javaslang in Spring Data Seq Return"); + } + + @Test + public void should_return_book(){ + ArrayList authors = new ArrayList(); + authors.add("author1"); + authors.add("author2"); + JavaBook testBook = new JavaBook(); + testBook.setTitle("Javaslang in Spring Data"); + testBook.setAuthors(authors); + JavaBook book = javaRepository.save(testBook); + JavaBook retBook = javaRepository.findById(1L); + + assertThat(retBook.getId()).isEqualTo(1L); + assertThat(retBook.getTitle()).isEqualTo("Javaslang in Spring Data"); + } +} \ No newline at end of file diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index e5389c1d349d..f5d7cf9effd8 100644 --- a/spring-data-mongodb/pom.xml +++ b/spring-data-mongodb/pom.xml @@ -20,6 +20,12 @@ org.springframework spring-core ${org.springframework.version} + + + commons-logging + commons-logging + + @@ -68,12 +74,6 @@ ${org.slf4j.version} - - org.slf4j - log4j-over-slf4j - ${org.slf4j.version} - - com.querydsl querydsl-mongodb diff --git a/spring-data-mongodb/src/main/resources/logback.xml b/spring-data-mongodb/src/main/resources/logback.xml index 215eeede64ee..ec0dc2469ae0 100644 --- a/spring-data-mongodb/src/main/resources/logback.xml +++ b/spring-data-mongodb/src/main/resources/logback.xml @@ -1,5 +1,5 @@ + - web - %date [%thread] %-5level %logger{36} - %message%n @@ -16,5 +16,4 @@ - \ No newline at end of file diff --git a/spring-data-neo4j/pom.xml b/spring-data-neo4j/pom.xml index 96606d597bcd..6f08fe293b59 100644 --- a/spring-data-neo4j/pom.xml +++ b/spring-data-neo4j/pom.xml @@ -10,31 +10,37 @@ org.neo4j neo4j - 3.1.0 + ${neo4j.version} org.neo4j neo4j-ogm-core - 2.1.1 + ${neo4j-ogm.version} org.neo4j neo4j-ogm-embedded-driver - 2.1.1 + ${neo4j-ogm.version} org.neo4j.driver neo4j-java-driver - 1.1.1 + ${neo4j-java-driver.version} org.springframework.data spring-data-neo4j - 4.2.0.RELEASE + ${spring-data-neo4j.version} + + + commons-logging + commons-logging + + @@ -70,12 +76,18 @@ neo4j-server ${neo4j.version} test-jar + + + commons-logging + commons-logging + + org.neo4j neo4j-ogm-test - ${neo4j-ogm-test.version} + ${neo4j-ogm.version} test @@ -95,6 +107,12 @@ org.springframework spring-test ${spring-test.version} + + + commons-logging + commons-logging + + @@ -160,12 +178,13 @@ UTF-8 UTF-8 + 1.1.1 3.1.0 4.1.6.RELEASE 1.1 1.4.3.RELEASE 4.3.5.RELEASE - 2.1.1 + 2.1.1 4.12 diff --git a/spring-data-neo4j/src/main/java/com/baeldung/spring/data/neo4j/config/MovieDatabaseNeo4jTestConfiguration.java b/spring-data-neo4j/src/main/java/com/baeldung/spring/data/neo4j/config/MovieDatabaseNeo4jTestConfiguration.java index 7bb1b78a0949..fda478e5be1e 100644 --- a/spring-data-neo4j/src/main/java/com/baeldung/spring/data/neo4j/config/MovieDatabaseNeo4jTestConfiguration.java +++ b/spring-data-neo4j/src/main/java/com/baeldung/spring/data/neo4j/config/MovieDatabaseNeo4jTestConfiguration.java @@ -5,20 +5,22 @@ import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; +import org.springframework.data.neo4j.config.Neo4jConfiguration; import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories; import org.springframework.transaction.annotation.EnableTransactionManagement; +@Configuration @EnableTransactionManagement @ComponentScan(basePackages = { "com.baeldung.spring.data.neo4j.services" }) -@Configuration @EnableNeo4jRepositories(basePackages = "com.baeldung.spring.data.neo4j.repostory") @Profile({ "embedded", "test" }) -public class MovieDatabaseNeo4jTestConfiguration { +public class MovieDatabaseNeo4jTestConfiguration extends Neo4jConfiguration { @Bean public org.neo4j.ogm.config.Configuration getConfiguration() { - org.neo4j.ogm.config.Configuration config = new org.neo4j.ogm.config.Configuration(); - config.driverConfiguration().setDriverClassName("org.neo4j.ogm.drivers.embedded.driver.EmbeddedDriver"); + final org.neo4j.ogm.config.Configuration config = new org.neo4j.ogm.config.Configuration(); + config.driverConfiguration() + .setDriverClassName("org.neo4j.ogm.drivers.embedded.driver.EmbeddedDriver"); return config; } @@ -26,4 +28,5 @@ public org.neo4j.ogm.config.Configuration getConfiguration() { public SessionFactory getSessionFactory() { return new SessionFactory(getConfiguration(), "com.baeldung.spring.data.neo4j.domain"); } + } diff --git a/spring-data-neo4j/src/main/resources/logback.xml b/spring-data-neo4j/src/main/resources/logback.xml index 215eeede64ee..ec0dc2469ae0 100644 --- a/spring-data-neo4j/src/main/resources/logback.xml +++ b/spring-data-neo4j/src/main/resources/logback.xml @@ -1,5 +1,5 @@ + - web - %date [%thread] %-5level %logger{36} - %message%n @@ -16,5 +16,4 @@ - \ No newline at end of file diff --git a/spring-data-neo4j/src/test/resources/logback.xml b/spring-data-neo4j/src/test/resources/logback.xml index 39a653832483..ec0dc2469ae0 100644 --- a/spring-data-neo4j/src/test/resources/logback.xml +++ b/spring-data-neo4j/src/test/resources/logback.xml @@ -1,16 +1,19 @@ - - + - %d %5p %40.40c:%4L - %m%n + web - %date [%thread] %-5level %logger{36} - %message%n + - + + - - - + + - + + + + \ No newline at end of file diff --git a/spring-data-redis/pom.xml b/spring-data-redis/pom.xml index 9700dd709186..f006026c81d3 100644 --- a/spring-data-redis/pom.xml +++ b/spring-data-redis/pom.xml @@ -13,7 +13,6 @@ 1.7.5.RELEASE 3.2.4 2.9.0 - 1.2.17 4.12 0.10.0 2.19.1 @@ -32,12 +31,6 @@ ${cglib.version} - - log4j - log4j - ${log4j.version} - - redis.clients jedis @@ -49,6 +42,12 @@ org.springframework spring-core ${spring.version} + + + commons-logging + commons-logging + + diff --git a/spring-data-redis/src/main/resources/logback.xml b/spring-data-redis/src/main/resources/logback.xml index 215eeede64ee..ec0dc2469ae0 100644 --- a/spring-data-redis/src/main/resources/logback.xml +++ b/spring-data-redis/src/main/resources/logback.xml @@ -1,5 +1,5 @@ + - web - %date [%thread] %-5level %logger{36} - %message%n @@ -16,5 +16,4 @@ - \ No newline at end of file diff --git a/spring-data-rest/README.md b/spring-data-rest/README.md index 4e8828a68847..fa4a264abbfc 100644 --- a/spring-data-rest/README.md +++ b/spring-data-rest/README.md @@ -15,3 +15,6 @@ To view the running application, visit [http://localhost:8080](http://localhost: ###Relevant Articles: - [Guide to Spring Data REST Validators](http://www.baeldung.com/spring-data-rest-validators) +- [Working with Relationships in Spring Data REST](http://www.baeldung.com/spring-data-rest-relationships) +- [AngularJS CRUD Application with Spring Data REST](http://www.baeldung.com/angularjs-crud-with-spring-data-rest) +- [List of In-Memory Databases](http://www.baeldung.com/java-in-memory-databases) diff --git a/spring-data-rest/pom.xml b/spring-data-rest/pom.xml index 4e8001ae7b1f..91b6d6187856 100644 --- a/spring-data-rest/pom.xml +++ b/spring-data-rest/pom.xml @@ -4,7 +4,7 @@ 4.0.0 com.baeldung - intro-spring-data-rest + spring-data-rest 1.0 jar @@ -14,7 +14,7 @@ org.springframework.boot spring-boot-starter-parent - 1.4.4.RELEASE + 1.5.2.RELEASE @@ -41,6 +41,26 @@ com.h2database h2 + + org.springframework.boot @@ -56,6 +76,17 @@ org.springframework.boot spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*IntegrationTest.java + **/*LiveTest.java + + + diff --git a/spring-data-rest/src/main/java/com/baeldung/config/DbConfig.java b/spring-data-rest/src/main/java/com/baeldung/config/DbConfig.java new file mode 100644 index 000000000000..8d1f9de4972b --- /dev/null +++ b/spring-data-rest/src/main/java/com/baeldung/config/DbConfig.java @@ -0,0 +1,61 @@ +package com.baeldung.config; + +import java.util.Properties; + +import javax.sql.DataSource; + +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.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; + +//@Configuration +@EnableJpaRepositories(basePackages = "com.baeldung.repositories") +// @PropertySource("persistence-h2.properties") +// @PropertySource("persistence-hsqldb.properties") +// @PropertySource("persistence-derby.properties") +public class DbConfig { + + @Autowired + private Environment env; + + @Bean + public DataSource dataSource() { + final DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName(env.getProperty("driverClassName")); + dataSource.setUrl(env.getProperty("url")); + dataSource.setUsername(env.getProperty("user")); + dataSource.setPassword(env.getProperty("password")); + return dataSource; + } + + @Bean + public LocalContainerEntityManagerFactoryBean entityManagerFactory() { + final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); + em.setDataSource(dataSource()); + em.setPackagesToScan(new String[] { "com.baeldung.models" }); + em.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); + em.setJpaProperties(additionalProperties()); + return em; + } + + final Properties additionalProperties() { + final Properties hibernateProperties = new Properties(); + if (env.getProperty("hibernate.hbm2ddl.auto") != null) { + hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto")); + } + if (env.getProperty("hibernate.dialect") != null) { + hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect")); + } + if (env.getProperty("hibernate.show_sql") != null) { + hibernateProperties.setProperty("hibernate.show_sql", env.getProperty("hibernate.show_sql")); + } + return hibernateProperties; + } + +} diff --git a/spring-rest-angular/src/main/java/org/baeldung/web/main/MvcConfig.java b/spring-data-rest/src/main/java/com/baeldung/config/MvcConfig.java similarity index 72% rename from spring-rest-angular/src/main/java/org/baeldung/web/main/MvcConfig.java rename to spring-data-rest/src/main/java/com/baeldung/config/MvcConfig.java index b24aad117710..bed1a6f84651 100644 --- a/spring-rest-angular/src/main/java/org/baeldung/web/main/MvcConfig.java +++ b/spring-data-rest/src/main/java/com/baeldung/config/MvcConfig.java @@ -1,4 +1,4 @@ -package org.baeldung.web.main; +package com.baeldung.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; @@ -11,7 +11,6 @@ @Configuration @EnableWebMvc -@ComponentScan("org.baeldung.web.controller") public class MvcConfig extends WebMvcConfigurerAdapter{ public MvcConfig(){ @@ -22,15 +21,5 @@ public MvcConfig(){ public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } - - @Bean - public ViewResolver viewResolver() { - final InternalResourceViewResolver bean = new InternalResourceViewResolver(); - - bean.setPrefix("/WEB-INF/pages/"); - bean.setSuffix(".html"); - - return bean; - } } diff --git a/spring-data-rest/src/main/java/com/baeldung/models/Address.java b/spring-data-rest/src/main/java/com/baeldung/models/Address.java index 98cf5f08699d..82e3783f3eaa 100644 --- a/spring-data-rest/src/main/java/com/baeldung/models/Address.java +++ b/spring-data-rest/src/main/java/com/baeldung/models/Address.java @@ -3,6 +3,7 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.OneToOne; @@ -10,7 +11,7 @@ public class Address { @Id - @GeneratedValue + @GeneratedValue(strategy=GenerationType.IDENTITY) private long id; @Column(nullable = false) diff --git a/spring-data-rest/src/main/java/com/baeldung/models/Author.java b/spring-data-rest/src/main/java/com/baeldung/models/Author.java index 7025fa4ad3c0..cdd04cbdcf01 100644 --- a/spring-data-rest/src/main/java/com/baeldung/models/Author.java +++ b/spring-data-rest/src/main/java/com/baeldung/models/Author.java @@ -6,6 +6,7 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; @@ -15,7 +16,7 @@ public class Author { @Id - @GeneratedValue + @GeneratedValue(strategy=GenerationType.IDENTITY) private long id; @Column(nullable = false) diff --git a/spring-data-rest/src/main/java/com/baeldung/models/Book.java b/spring-data-rest/src/main/java/com/baeldung/models/Book.java index 8f836a259a72..06abfb8f4d65 100644 --- a/spring-data-rest/src/main/java/com/baeldung/models/Book.java +++ b/spring-data-rest/src/main/java/com/baeldung/models/Book.java @@ -5,6 +5,7 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToMany; @@ -14,7 +15,7 @@ public class Book { @Id - @GeneratedValue + @GeneratedValue(strategy=GenerationType.IDENTITY) private long id; @Column(nullable = false) diff --git a/spring-data-rest/src/main/java/com/baeldung/models/Library.java b/spring-data-rest/src/main/java/com/baeldung/models/Library.java index 61eead16eafe..c27512d0e40e 100644 --- a/spring-data-rest/src/main/java/com/baeldung/models/Library.java +++ b/spring-data-rest/src/main/java/com/baeldung/models/Library.java @@ -5,6 +5,7 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.OneToMany; @@ -16,7 +17,7 @@ public class Library { @Id - @GeneratedValue + @GeneratedValue(strategy=GenerationType.IDENTITY) private long id; @Column diff --git a/spring-data-rest/src/main/java/com/baeldung/repositories/UserRepository.java b/spring-data-rest/src/main/java/com/baeldung/repositories/UserRepository.java index 0b55ac89b6cc..2bd9c025ddf6 100644 --- a/spring-data-rest/src/main/java/com/baeldung/repositories/UserRepository.java +++ b/spring-data-rest/src/main/java/com/baeldung/repositories/UserRepository.java @@ -2,9 +2,10 @@ import org.springframework.data.repository.CrudRepository; import org.springframework.data.rest.core.annotation.RepositoryRestResource; - +import org.springframework.web.bind.annotation.CrossOrigin; import com.baeldung.models.WebsiteUser; +@CrossOrigin @RepositoryRestResource(collectionResourceRel = "users", path = "users") public interface UserRepository extends CrudRepository { diff --git a/spring-data-rest/src/main/resources/persistence-derby.properties b/spring-data-rest/src/main/resources/persistence-derby.properties new file mode 100644 index 000000000000..9bcd91c6f95e --- /dev/null +++ b/spring-data-rest/src/main/resources/persistence-derby.properties @@ -0,0 +1,8 @@ +driverClassName=org.apache.derby.jdbc.EmbeddedDriver +url=jdbc:derby:memory:myD;create=true +username=sa +password= + +hibernate.dialect=org.hibernate.dialect.DerbyDialect +hibernate.show_sql=true +hibernate.hbm2ddl.auto=create-drop \ No newline at end of file diff --git a/spring-data-rest/src/main/resources/persistence-h2.properties b/spring-data-rest/src/main/resources/persistence-h2.properties new file mode 100644 index 000000000000..d535f9dbe47a --- /dev/null +++ b/spring-data-rest/src/main/resources/persistence-h2.properties @@ -0,0 +1,8 @@ +driverClassName=org.h2.Driver +url=jdbc:h2:mem:myDb;DB_CLOSE_DELAY=-1 +username=sa +password= + +hibernate.dialect=org.hibernate.dialect.H2Dialect +hibernate.show_sql=true +hibernate.hbm2ddl.auto=create-drop \ No newline at end of file diff --git a/spring-data-rest/src/main/resources/persistence-hsqldb.properties b/spring-data-rest/src/main/resources/persistence-hsqldb.properties new file mode 100644 index 000000000000..00464f102662 --- /dev/null +++ b/spring-data-rest/src/main/resources/persistence-hsqldb.properties @@ -0,0 +1,8 @@ +driverClassName=org.hsqldb.jdbc.JDBCDriver +url=jdbc:hsqldb:mem:myDb +username=sa +password= + +hibernate.dialect=org.hibernate.dialect.HSQLDialect +hibernate.show_sql=true +hibernate.hbm2ddl.auto=create-drop \ No newline at end of file diff --git a/spring-data-rest/src/main/resources/persistence-sqlite.properties b/spring-data-rest/src/main/resources/persistence-sqlite.properties new file mode 100644 index 000000000000..018c2cbacae4 --- /dev/null +++ b/spring-data-rest/src/main/resources/persistence-sqlite.properties @@ -0,0 +1,4 @@ +driverClassName=org.sqlite.JDBC +url=jdbc:sqlite:memory:myDb +username=sa +password=sa \ No newline at end of file diff --git a/spring-data-rest/src/main/webapp/users.html b/spring-data-rest/src/main/webapp/users.html new file mode 100644 index 000000000000..c153f4a36ec9 --- /dev/null +++ b/spring-data-rest/src/main/webapp/users.html @@ -0,0 +1,51 @@ + + + + +User CRUD + + + + + +
+ + + + + + + + + + + + + +
ID:
Name:
Email:
+

+ Get User + Update User + Add User + Delete User + +

+

{{message}}

+

{{errorMessage}}

+ +
+
+ Get all Users
+

+
+ {{usr.name}} {{usr.email}} +
+
+ + \ No newline at end of file diff --git a/spring-data-rest/src/main/webapp/view/app.js b/spring-data-rest/src/main/webapp/view/app.js new file mode 100644 index 000000000000..0bdc6e197957 --- /dev/null +++ b/spring-data-rest/src/main/webapp/view/app.js @@ -0,0 +1,122 @@ +var app = angular.module('app',[]); + +app.controller('UserCRUDCtrl', ['$scope','UserCRUDService', function ($scope,UserCRUDService) { + + $scope.updateUser = function () { + UserCRUDService.updateUser($scope.user.id,$scope.user.name,$scope.user.email) + .then(function success(response){ + $scope.message = 'User data updated!'; + $scope.errorMessage = ''; + }, + function error(response){ + $scope.errorMessage = 'Error updating user!'; + $scope.message = ''; + }); + } + + $scope.getUser = function () { + var id = $scope.user.id; + UserCRUDService.getUser($scope.user.id) + .then(function success(response){ + $scope.user = response.data; + $scope.user.id = id; + $scope.message=''; + $scope.errorMessage = ''; + }, + function error (response ){ + $scope.message = ''; + if (response.status === 404){ + $scope.errorMessage = 'User not found!'; + } + else { + $scope.errorMessage = "Error getting user!"; + } + }); + } + + $scope.addUser = function () { + if ($scope.user != null && $scope.user.name) { + UserCRUDService.addUser($scope.user.name, $scope.user.email) + .then (function success(response){ + $scope.message = 'User added!'; + $scope.errorMessage = ''; + }, + function error(response){ + $scope.errorMessage = 'Error adding user!'; + $scope.message = ''; + }); + } + else { + $scope.errorMessage = 'Please enter a name!'; + $scope.message = ''; + } + } + + $scope.deleteUser = function () { + UserCRUDService.deleteUser($scope.user.id) + .then (function success(response){ + $scope.message = 'User deleted!'; + $scope.user = null; + $scope.errorMessage=''; + }, + function error(response){ + $scope.errorMessage = 'Error deleting user!'; + $scope.message=''; + }) + } + + $scope.getAllUsers = function () { + UserCRUDService.getAllUsers() + .then(function success(response){ + $scope.users = response.data._embedded.users; + $scope.message=''; + $scope.errorMessage = ''; + }, + function error (response ){ + $scope.message=''; + $scope.errorMessage = 'Error getting users!'; + }); + } + +}]); + +app.service('UserCRUDService',['$http', function ($http) { + + this.getUser = function getUser(userId){ + return $http({ + method: 'GET', + url: 'users/'+userId + }); + } + + this.addUser = function addUser(name, email){ + return $http({ + method: 'POST', + url: 'users', + data: {name:name, email:email} + }); + } + + this.deleteUser = function deleteUser(id){ + return $http({ + method: 'DELETE', + url: 'users/'+id + }) + } + + this.updateUser = function updateUser(id,name,email){ + return $http({ + method: 'PATCH', + url: 'users/'+id, + data: {name:name, email:email} + }) + } + + this.getAllUsers = function getAllUsers(){ + return $http({ + method: 'GET', + url: 'users' + }); + } + +}]); \ No newline at end of file diff --git a/spring-data-rest/src/test/java/com/baeldung/relationships/SpringDataRelationshipsTest.java b/spring-data-rest/src/test/java/com/baeldung/relationships/SpringDataRelationshipsIntegrationTest.java similarity index 93% rename from spring-data-rest/src/test/java/com/baeldung/relationships/SpringDataRelationshipsTest.java rename to spring-data-rest/src/test/java/com/baeldung/relationships/SpringDataRelationshipsIntegrationTest.java index 21a067a6453f..e3fe60d48766 100644 --- a/spring-data-rest/src/test/java/com/baeldung/relationships/SpringDataRelationshipsTest.java +++ b/spring-data-rest/src/test/java/com/baeldung/relationships/SpringDataRelationshipsIntegrationTest.java @@ -18,12 +18,13 @@ import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.test.context.junit4.SpringRunner; +import org.json.JSONException; import static org.junit.Assert.assertEquals; @RunWith(SpringRunner.class) @SpringBootTest(classes = SpringDataRestApplication.class, webEnvironment = WebEnvironment.DEFINED_PORT) -public class SpringDataRelationshipsTest { +public class SpringDataRelationshipsIntegrationTest { @Autowired private TestRestTemplate template; @@ -37,7 +38,7 @@ public class SpringDataRelationshipsTest { private static final String AUTHOR_NAME = "George Orwell"; @Test - public void whenSaveOneToOneRelationship_thenCorrect() { + public void whenSaveOneToOneRelationship_thenCorrect() throws JSONException { Library library = new Library(LIBRARY_NAME); template.postForEntity(LIBRARY_ENDPOINT, library, Library.class); @@ -55,7 +56,7 @@ public void whenSaveOneToOneRelationship_thenCorrect() { } @Test - public void whenSaveOneToManyRelationship_thenCorrect() { + public void whenSaveOneToManyRelationship_thenCorrect() throws JSONException{ Library library = new Library(LIBRARY_NAME); template.postForEntity(LIBRARY_ENDPOINT, library, Library.class); @@ -77,7 +78,7 @@ public void whenSaveOneToManyRelationship_thenCorrect() { } @Test - public void whenSaveManyToManyRelationship_thenCorrect() { + public void whenSaveManyToManyRelationship_thenCorrect() throws JSONException{ Author author1 = new Author(AUTHOR_NAME); template.postForEntity(AUTHOR_ENDPOINT, author1, Author.class); diff --git a/spring-data-rest/src/test/java/com/baeldung/validator/SpringDataRestValidatorTest.java b/spring-data-rest/src/test/java/com/baeldung/validator/SpringDataRestValidatorIntegrationTest.java similarity index 98% rename from spring-data-rest/src/test/java/com/baeldung/validator/SpringDataRestValidatorTest.java rename to spring-data-rest/src/test/java/com/baeldung/validator/SpringDataRestValidatorIntegrationTest.java index 300fc081d309..bc321bc68690 100644 --- a/spring-data-rest/src/test/java/com/baeldung/validator/SpringDataRestValidatorTest.java +++ b/spring-data-rest/src/test/java/com/baeldung/validator/SpringDataRestValidatorIntegrationTest.java @@ -1,11 +1,8 @@ package com.baeldung.validator; -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.redirectedUrl; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup; - +import com.baeldung.SpringDataRestApplication; +import com.baeldung.models.WebsiteUser; +import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -17,14 +14,16 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.web.context.WebApplicationContext; -import com.baeldung.SpringDataRestApplication; -import com.baeldung.models.WebsiteUser; -import com.fasterxml.jackson.databind.ObjectMapper; +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.redirectedUrl; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup; @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = SpringDataRestApplication.class) @WebAppConfiguration -public class SpringDataRestValidatorTest { +public class SpringDataRestValidatorIntegrationTest { public static final String URL = "http://localhost"; private MockMvc mockMvc; diff --git a/spring-data-solr/pom.xml b/spring-data-solr/pom.xml index e43b3ff7744c..54fc0d329419 100644 --- a/spring-data-solr/pom.xml +++ b/spring-data-solr/pom.xml @@ -14,7 +14,6 @@ 4.3.4.RELEASE 2.19.1 2.0.5.RELEASE - 1.2.17 4.12 @@ -23,6 +22,12 @@ org.springframework spring-core ${spring.version} + + + commons-logging + commons-logging + + org.springframework.data @@ -34,11 +39,6 @@ spring-context ${spring.version} - - log4j - log4j - ${log4j.version} - junit junit @@ -72,38 +72,4 @@ - - - integration - - - - org.apache.maven.plugins - maven-surefire-plugin - - - integration-test - - test - - - - **/*LiveTest.java - - - **/*IntegrationTest.java - - - - - - - json - - - - - - - \ No newline at end of file diff --git a/spring-dispatcher-servlet/pom.xml b/spring-dispatcher-servlet/pom.xml index 13ad06219565..b6210971aee0 100644 --- a/spring-dispatcher-servlet/pom.xml +++ b/spring-dispatcher-servlet/pom.xml @@ -25,6 +25,12 @@ org.springframework spring-webmvc ${org.springframework.version} + + + commons-logging + commons-logging + + org.thymeleaf @@ -36,16 +42,7 @@ slf4j-api ${org.slf4j.version} - - org.apache.logging.log4j - log4j-core - ${log4j.version} - - - org.apache.logging.log4j - log4j-slf4j-impl - ${log4j.version} - + @@ -87,7 +84,6 @@ 1.7.21 - 2.7 3.1.0 diff --git a/spring-dispatcher-servlet/src/main/resources/log4j2.xml b/spring-dispatcher-servlet/src/main/resources/log4j2.xml deleted file mode 100644 index fb18e8279ad9..000000000000 --- a/spring-dispatcher-servlet/src/main/resources/log4j2.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/spring-exceptions/pom.xml b/spring-exceptions/pom.xml index a8254151aa1d..8733f8f2f497 100644 --- a/spring-exceptions/pom.xml +++ b/spring-exceptions/pom.xml @@ -15,11 +15,23 @@ org.springframework spring-web ${org.springframework.version} + + + commons-logging + commons-logging + + org.springframework spring-webmvc ${org.springframework.version} + + + commons-logging + commons-logging + + org.springframework diff --git a/spring-exceptions/src/main/resources/logback.xml b/spring-exceptions/src/main/resources/logback.xml index 45c9697f776f..ec0dc2469ae0 100644 --- a/spring-exceptions/src/main/resources/logback.xml +++ b/spring-exceptions/src/main/resources/logback.xml @@ -1,22 +1,19 @@ + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - + + - - - - - - - - - - - + + + + + \ No newline at end of file diff --git a/spring-freemarker/src/main/resources/log4j.xml b/spring-freemarker/src/main/resources/log4j.xml deleted file mode 100644 index ae7d4b5ca4e5..000000000000 --- a/spring-freemarker/src/main/resources/log4j.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/spring-freemarker/src/test/resources/log4j.xml b/spring-freemarker/src/test/resources/log4j.xml deleted file mode 100644 index 9b1538d31a4f..000000000000 --- a/spring-freemarker/src/test/resources/log4j.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/spring-groovy/.gitignore b/spring-groovy/.gitignore new file mode 100644 index 000000000000..c17c2273059c --- /dev/null +++ b/spring-groovy/.gitignore @@ -0,0 +1,7 @@ +/target/ +/project/ +.classpath +.settings +.eclipse +.idea +.project diff --git a/spring-groovy/pom.xml b/spring-groovy/pom.xml new file mode 100644 index 000000000000..686633da272e --- /dev/null +++ b/spring-groovy/pom.xml @@ -0,0 +1,35 @@ + + 4.0.0 + + com.baeldug + spring-groovy + 0.0.1-SNAPSHOT + jar + + spring-groovy + http://maven.apache.org + + + UTF-8 + + + + + junit + junit + 3.8.1 + test + + + org.springframework + spring-core + 4.3.6.RELEASE + + + org.springframework.integration + spring-integration-groovy + 4.3.7.RELEASE + + + diff --git a/spring-groovy/src/main/java/com/baeldug/spring_groovy/App.java b/spring-groovy/src/main/java/com/baeldug/spring_groovy/App.java new file mode 100644 index 000000000000..1df6681c4275 --- /dev/null +++ b/spring-groovy/src/main/java/com/baeldug/spring_groovy/App.java @@ -0,0 +1,13 @@ +package com.baeldug.spring_groovy; + +/** + * Hello world! + * + */ +public class App +{ + public static void main( String[] args ) + { + System.out.println( "Hello World!" ); + } +} diff --git a/spring-groovy/src/main/java/com/baeldug/spring_groovy/TestConfig.java b/spring-groovy/src/main/java/com/baeldug/spring_groovy/TestConfig.java new file mode 100644 index 000000000000..474216de4ecd --- /dev/null +++ b/spring-groovy/src/main/java/com/baeldug/spring_groovy/TestConfig.java @@ -0,0 +1,8 @@ +package com.baeldug.spring_groovy; + +import org.springframework.stereotype.Component; + +@Component +public class TestConfig { + +} diff --git a/spring-groovy/src/main/resources/groovyContextConfig.groovy b/spring-groovy/src/main/resources/groovyContextConfig.groovy new file mode 100644 index 000000000000..3ac736687681 --- /dev/null +++ b/spring-groovy/src/main/resources/groovyContextConfig.groovy @@ -0,0 +1,5 @@ +beans{ + testString String, 'test' + testNum int, 100 + testObj(i:101,s:'objVal') +} \ No newline at end of file diff --git a/spring-groovy/src/test/java/com/baeldug/spring_groovy/AppTest.java b/spring-groovy/src/test/java/com/baeldug/spring_groovy/AppTest.java new file mode 100644 index 000000000000..19eefb6c0f49 --- /dev/null +++ b/spring-groovy/src/test/java/com/baeldug/spring_groovy/AppTest.java @@ -0,0 +1,38 @@ +package com.baeldug.spring_groovy; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * Unit test for simple App. + */ +public class AppTest + extends TestCase +{ + /** + * Create the test case + * + * @param testName name of the test case + */ + public AppTest( String testName ) + { + super( testName ); + } + + /** + * @return the suite of tests being tested + */ + public static Test suite() + { + return new TestSuite( AppTest.class ); + } + + /** + * Rigourous Test :-) + */ + public void testApp() + { + assertTrue( true ); + } +} diff --git a/spring-hibernate3/pom.xml b/spring-hibernate3/pom.xml index f7b198a2b165..c45c9bb15e5b 100644 --- a/spring-hibernate3/pom.xml +++ b/spring-hibernate3/pom.xml @@ -14,6 +14,12 @@ org.springframework spring-context ${org.springframework.version} + + + commons-logging + commons-logging + + diff --git a/spring-hibernate3/src/main/resources/logback.xml b/spring-hibernate3/src/main/resources/logback.xml index 1146dade632e..ec0dc2469ae0 100644 --- a/spring-hibernate3/src/main/resources/logback.xml +++ b/spring-hibernate3/src/main/resources/logback.xml @@ -1,20 +1,19 @@ + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - + + - - - - - - - - - + + + + + \ No newline at end of file diff --git a/spring-hibernate4/README.md b/spring-hibernate4/README.md index 02888c4ad0e4..08cfe2b5387f 100644 --- a/spring-hibernate4/README.md +++ b/spring-hibernate4/README.md @@ -13,6 +13,7 @@ - [Eager/Lazy Loading In Hibernate](http://www.baeldung.com/hibernate-lazy-eager-loading) - [Hibernate Criteria Queries](http://www.baeldung.com/hibernate-criteria-queries) - [Hibernate One to Many Annotation Tutorial](http://www.baeldung.com/hibernate-one-to-many) +- [Guide to @Immutable Annotation in Hibernate](http://www.baeldung.com/hibernate-immutable) ### Quick Start diff --git a/spring-hibernate4/pom.xml b/spring-hibernate4/pom.xml index dbacb3a60a72..8ba0a3700cc4 100644 --- a/spring-hibernate4/pom.xml +++ b/spring-hibernate4/pom.xml @@ -14,6 +14,12 @@ org.springframework spring-context ${org.springframework.version} + + + commons-logging + commons-logging + + org.springframework diff --git a/spring-hibernate4/src/main/resources/immutable.cfg.xml b/spring-hibernate4/src/main/resources/immutable.cfg.xml new file mode 100644 index 000000000000..fe1e3cb72348 --- /dev/null +++ b/spring-hibernate4/src/main/resources/immutable.cfg.xml @@ -0,0 +1,38 @@ + + + + + + + + + org.hsqldb.jdbcDriver + jdbc:hsqldb:hsql:mem://localhost/xdb + sa + + + + 1 + + + org.hibernate.dialect.HSQLDialect + + + thread + + + org.hibernate.cache.NoCacheProvider + + + true + + + update + + + + + + \ No newline at end of file diff --git a/spring-hibernate4/src/main/resources/logback.xml b/spring-hibernate4/src/main/resources/logback.xml index 71a6d50a58ae..ec0dc2469ae0 100644 --- a/spring-hibernate4/src/main/resources/logback.xml +++ b/spring-hibernate4/src/main/resources/logback.xml @@ -1,22 +1,19 @@ + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - + + - - - - - - - - - - - + + + + + \ No newline at end of file diff --git a/spring-hibernate4/src/test/java/com/baeldung/persistence/save/SaveMethodsTest.java b/spring-hibernate4/src/test/java/com/baeldung/persistence/save/SaveMethodsIntegrationTest.java similarity index 99% rename from spring-hibernate4/src/test/java/com/baeldung/persistence/save/SaveMethodsTest.java rename to spring-hibernate4/src/test/java/com/baeldung/persistence/save/SaveMethodsIntegrationTest.java index 2e729c5680a1..ef83af3a0d16 100644 --- a/spring-hibernate4/src/test/java/com/baeldung/persistence/save/SaveMethodsTest.java +++ b/spring-hibernate4/src/test/java/com/baeldung/persistence/save/SaveMethodsIntegrationTest.java @@ -16,7 +16,7 @@ * Testing specific implementation details for different methods: * persist, save, merge, update, saveOrUpdate. */ -public class SaveMethodsTest { +public class SaveMethodsIntegrationTest { private static SessionFactory sessionFactory; diff --git a/spring-hibernate5/README.md b/spring-hibernate5/README.md new file mode 100644 index 000000000000..fd539fdcb6f9 --- /dev/null +++ b/spring-hibernate5/README.md @@ -0,0 +1,3 @@ +### Relevant articles + +- [Guide to @Immutable Annotation in Hibernate](http://www.baeldung.com/hibernate-immutable) diff --git a/spring-hibernate5/pom.xml b/spring-hibernate5/pom.xml index 81f8084d748d..ed127c34c19f 100644 --- a/spring-hibernate5/pom.xml +++ b/spring-hibernate5/pom.xml @@ -14,6 +14,12 @@ org.springframework spring-context ${org.springframework.version} + + + commons-logging + commons-logging + + org.springframework @@ -148,7 +154,6 @@ org.hsqldb hsqldb ${hsqldb.version} - test diff --git a/spring-hibernate5/src/main/java/com/baeldung/hibernate/criteria/view/ApplicationView.java b/spring-hibernate5/src/main/java/com/baeldung/hibernate/criteria/view/ApplicationView.java index a854b51753ea..7b53a5f7525d 100644 --- a/spring-hibernate5/src/main/java/com/baeldung/hibernate/criteria/view/ApplicationView.java +++ b/spring-hibernate5/src/main/java/com/baeldung/hibernate/criteria/view/ApplicationView.java @@ -27,11 +27,7 @@ import com.baeldung.hibernate.criteria.util.HibernateUtil; public class ApplicationView { - - public ApplicationView() { - - } - + @SuppressWarnings("unchecked") public boolean checkIfCriteriaTimeLower() { final Session session = HibernateUtil.getHibernateSession(); @@ -181,8 +177,7 @@ public String[] twoCriteria() { CriteriaBuilder builder = session.getCriteriaBuilder(); CriteriaQuery criteriaItem = builder.createQuery(Item.class); Root rootItem = criteriaItem.from(Item.class); - criteriaItem.select(rootItem).where(builder.isNull(rootItem.get("itemDescription"))) - .where(builder.like(rootItem.get("itemName"), "chair%")); + criteriaItem.select(rootItem).where(builder.isNull(rootItem.get("itemDescription"))).where(builder.like(rootItem.get("itemName"), "chair%")); final List notNullItemsList = session.createQuery(criteriaItem).getResultList(); final String notNullDescItems[] = new String[notNullItemsList.size()]; for (int i = 0; i < notNullItemsList.size(); i++) { diff --git a/spring-hibernate5/src/main/java/com/baeldung/hibernate/immutable/entities/Event.java b/spring-hibernate5/src/main/java/com/baeldung/hibernate/immutable/entities/Event.java new file mode 100644 index 000000000000..2928ffe98178 --- /dev/null +++ b/spring-hibernate5/src/main/java/com/baeldung/hibernate/immutable/entities/Event.java @@ -0,0 +1,58 @@ +package com.baeldung.hibernate.immutable.entities; + +import org.hibernate.annotations.Cascade; +import org.hibernate.annotations.CascadeType; +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.annotations.Immutable; + +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Set; + +@Entity +@Immutable +@Table(name = "events") +public class Event { + + @Id + @Column(name = "event_id") + @GeneratedValue(generator = "increment") + @GenericGenerator(name = "increment", strategy = "increment") + private Long id; + + @Column(name = "title") + private String title; + + @ElementCollection + @Immutable + private Set guestList; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + @Cascade({ CascadeType.SAVE_UPDATE, CascadeType.DELETE }) + public Set getGuestList() { + return guestList; + } + + public void setGuestList(Set guestList) { + this.guestList = guestList; + } +} diff --git a/spring-hibernate5/src/main/java/com/baeldung/hibernate/immutable/util/HibernateUtil.java b/spring-hibernate5/src/main/java/com/baeldung/hibernate/immutable/util/HibernateUtil.java new file mode 100644 index 000000000000..e4a2319c379f --- /dev/null +++ b/spring-hibernate5/src/main/java/com/baeldung/hibernate/immutable/util/HibernateUtil.java @@ -0,0 +1,29 @@ +package com.baeldung.hibernate.immutable.util; + +import com.baeldung.hibernate.immutable.entities.Event; +import org.hibernate.SessionFactory; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.Configuration; +import org.hibernate.service.ServiceRegistry; + +public class HibernateUtil { + private static final SessionFactory sessionFactory = buildSessionFactory(); + + private static SessionFactory buildSessionFactory() { + try { + // Create a session factory from immutable.cfg.xml + Configuration configuration = new Configuration(); + configuration.addAnnotatedClass(Event.class); + configuration.configure("immutable.cfg.xml"); + ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build(); + return configuration.buildSessionFactory(serviceRegistry); + } catch (Throwable ex) { + System.out.println("Initial SessionFactory creation failed." + ex); + throw new ExceptionInInitializerError(ex); + } + } + + public static SessionFactory getSessionFactory() { + return sessionFactory; + } +} diff --git a/spring-hibernate5/src/main/java/com/baeldung/persistence/service/common/AbstractHibernateAuditableService.java b/spring-hibernate5/src/main/java/com/baeldung/persistence/service/common/AbstractHibernateAuditableService.java index 8e2df155191b..2695d7760af2 100644 --- a/spring-hibernate5/src/main/java/com/baeldung/persistence/service/common/AbstractHibernateAuditableService.java +++ b/spring-hibernate5/src/main/java/com/baeldung/persistence/service/common/AbstractHibernateAuditableService.java @@ -8,8 +8,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional(value = "hibernateTransactionManager") -public abstract class AbstractHibernateAuditableService extends AbstractHibernateService - implements IOperations, IAuditOperations { +public abstract class AbstractHibernateAuditableService extends AbstractHibernateService implements IOperations, IAuditOperations { @Override public List getEntitiesAtRevision(final Number revision) { diff --git a/spring-hibernate5/src/main/java/com/baeldung/persistence/service/common/AbstractHibernateService.java b/spring-hibernate5/src/main/java/com/baeldung/persistence/service/common/AbstractHibernateService.java index 5da2f299f13b..02b8ccf48bdf 100644 --- a/spring-hibernate5/src/main/java/com/baeldung/persistence/service/common/AbstractHibernateService.java +++ b/spring-hibernate5/src/main/java/com/baeldung/persistence/service/common/AbstractHibernateService.java @@ -7,8 +7,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional(value = "hibernateTransactionManager") -public abstract class AbstractHibernateService extends AbstractService implements - IOperations { +public abstract class AbstractHibernateService extends AbstractService implements IOperations { @Override public T findOne(final long id) { diff --git a/spring-hibernate5/src/main/resources/immutable.cfg.xml b/spring-hibernate5/src/main/resources/immutable.cfg.xml new file mode 100644 index 000000000000..a572ebeac26f --- /dev/null +++ b/spring-hibernate5/src/main/resources/immutable.cfg.xml @@ -0,0 +1,32 @@ + + + + + + + + + org.hsqldb.jdbcDriver + jdbc:hsqldb:mem:test + sa + + + + 1 + + + org.hibernate.dialect.HSQLDialect + + + thread + + + true + + + update + + + \ No newline at end of file diff --git a/spring-hibernate5/src/main/resources/logback.xml b/spring-hibernate5/src/main/resources/logback.xml index 71a6d50a58ae..ec0dc2469ae0 100644 --- a/spring-hibernate5/src/main/resources/logback.xml +++ b/spring-hibernate5/src/main/resources/logback.xml @@ -1,22 +1,19 @@ + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - + + - - - - - - - - - - - + + + + + \ No newline at end of file diff --git a/spring-hibernate5/src/test/java/com/baeldung/hibernate/criteria/HibernateCriteriaIntegrationTest.java b/spring-hibernate5/src/test/java/com/baeldung/hibernate/criteria/HibernateCriteriaIntegrationTest.java index 7caa02f15671..c7d59e3b004d 100644 --- a/spring-hibernate5/src/test/java/com/baeldung/hibernate/criteria/HibernateCriteriaIntegrationTest.java +++ b/spring-hibernate5/src/test/java/com/baeldung/hibernate/criteria/HibernateCriteriaIntegrationTest.java @@ -24,8 +24,7 @@ public void testPerformanceOfCriteria() { @Test public void testLikeCriteriaQuery() { final Session session = HibernateUtil.getHibernateSession(); - final List expectedLikeList = session.createQuery("From Item where itemName like '%chair%'") - .getResultList(); + final List expectedLikeList = session.createQuery("From Item where itemName like '%chair%'").getResultList(); final String expectedLikeItems[] = new String[expectedLikeList.size()]; for (int i = 0; i < expectedLikeList.size(); i++) { expectedLikeItems[i] = expectedLikeList.get(i).getItemName(); @@ -37,8 +36,7 @@ public void testLikeCriteriaQuery() { @Test public void testILikeCriteriaQuery() { final Session session = HibernateUtil.getHibernateSession(); - final List expectedChairCaseList = session.createQuery("From Item where itemName like '%Chair%'") - .getResultList(); + final List expectedChairCaseList = session.createQuery("From Item where itemName like '%Chair%'").getResultList(); final String expectedChairCaseItems[] = new String[expectedChairCaseList.size()]; for (int i = 0; i < expectedChairCaseList.size(); i++) { expectedChairCaseItems[i] = expectedChairCaseList.get(i).getItemName(); @@ -51,8 +49,7 @@ public void testILikeCriteriaQuery() { @Test public void testNullCriteriaQuery() { final Session session = HibernateUtil.getHibernateSession(); - final List expectedIsNullDescItemsList = session.createQuery("From Item where itemDescription is null") - .getResultList(); + final List expectedIsNullDescItemsList = session.createQuery("From Item where itemDescription is null").getResultList(); final String expectedIsNullDescItems[] = new String[expectedIsNullDescItemsList.size()]; for (int i = 0; i < expectedIsNullDescItemsList.size(); i++) { expectedIsNullDescItems[i] = expectedIsNullDescItemsList.get(i).getItemName(); @@ -64,8 +61,7 @@ public void testNullCriteriaQuery() { @Test public void testIsNotNullCriteriaQuery() { final Session session = HibernateUtil.getHibernateSession(); - final List expectedIsNotNullDescItemsList = session.createQuery( - "From Item where itemDescription is not null").getResultList(); + final List expectedIsNotNullDescItemsList = session.createQuery("From Item where itemDescription is not null").getResultList(); final String expectedIsNotNullDescItems[] = new String[expectedIsNotNullDescItemsList.size()]; for (int i = 0; i < expectedIsNotNullDescItemsList.size(); i++) { expectedIsNotNullDescItems[i] = expectedIsNotNullDescItemsList.get(i).getItemName(); @@ -78,8 +74,7 @@ public void testIsNotNullCriteriaQuery() { @Test public void testAverageProjection() { final Session session = HibernateUtil.getHibernateSession(); - final List expectedAvgProjItemsList = session.createQuery("Select avg(itemPrice) from Item item") - .getResultList(); + final List expectedAvgProjItemsList = session.createQuery("Select avg(itemPrice) from Item item").getResultList(); final Double expectedAvgProjItems[] = new Double[expectedAvgProjItemsList.size()]; for (int i = 0; i < expectedAvgProjItemsList.size(); i++) { @@ -105,8 +100,7 @@ public void testRowCountProjection() { @Test public void testOrCriteriaQuery() { final Session session = HibernateUtil.getHibernateSession(); - final List expectedOrCritItemsList = session.createQuery( - "From Item where itemPrice>1000 or itemName like 'Chair%'").getResultList(); + final List expectedOrCritItemsList = session.createQuery("From Item where itemPrice>1000 or itemName like 'Chair%'").getResultList(); final String expectedOrCritItems[] = new String[expectedOrCritItemsList.size()]; for (int i = 0; i < expectedOrCritItemsList.size(); i++) { expectedOrCritItems[i] = expectedOrCritItemsList.get(i).getItemName(); @@ -118,8 +112,7 @@ public void testOrCriteriaQuery() { @Test public void testAndCriteriaQuery() { final Session session = HibernateUtil.getHibernateSession(); - final List expectedAndCritItemsList = session.createQuery( - "From Item where itemPrice>1000 and itemName like 'Chair%'").getResultList(); + final List expectedAndCritItemsList = session.createQuery("From Item where itemPrice>1000 and itemName like 'Chair%'").getResultList(); final String expectedAndCritItems[] = new String[expectedAndCritItemsList.size()]; for (int i = 0; i < expectedAndCritItemsList.size(); i++) { expectedAndCritItems[i] = expectedAndCritItemsList.get(i).getItemName(); @@ -131,8 +124,7 @@ public void testAndCriteriaQuery() { @Test public void testMultiCriteriaQuery() { final Session session = HibernateUtil.getHibernateSession(); - final List expectedMultiCritItemsList = session.createQuery( - "From Item where itemDescription is null and itemName like'chair%'").getResultList(); + final List expectedMultiCritItemsList = session.createQuery("From Item where itemDescription is null and itemName like'chair%'").getResultList(); final String expectedMultiCritItems[] = new String[expectedMultiCritItemsList.size()]; for (int i = 0; i < expectedMultiCritItemsList.size(); i++) { expectedMultiCritItems[i] = expectedMultiCritItemsList.get(i).getItemName(); @@ -144,8 +136,7 @@ public void testMultiCriteriaQuery() { @Test public void testSortCriteriaQuery() { final Session session = HibernateUtil.getHibernateSession(); - final List expectedSortCritItemsList = session.createQuery( - "From Item order by itemName asc, itemPrice desc").getResultList(); + final List expectedSortCritItemsList = session.createQuery("From Item order by itemName asc, itemPrice desc").getResultList(); final String expectedSortCritItems[] = new String[expectedSortCritItemsList.size()]; for (int i = 0; i < expectedSortCritItemsList.size(); i++) { expectedSortCritItems[i] = expectedSortCritItemsList.get(i).getItemName(); @@ -157,8 +148,7 @@ public void testSortCriteriaQuery() { @Test public void testGreaterThanCriteriaQuery() { final Session session = HibernateUtil.getHibernateSession(); - final List expectedGreaterThanList = session.createQuery("From Item where itemPrice>1000") - .getResultList(); + final List expectedGreaterThanList = session.createQuery("From Item where itemPrice>1000").getResultList(); final String expectedGreaterThanItems[] = new String[expectedGreaterThanList.size()]; for (int i = 0; i < expectedGreaterThanList.size(); i++) { expectedGreaterThanItems[i] = expectedGreaterThanList.get(i).getItemName(); @@ -182,8 +172,7 @@ public void testLessThanCriteriaQuery() { @Test public void betweenCriteriaQuery() { final Session session = HibernateUtil.getHibernateSession(); - final List expectedBetweenList = session.createQuery("From Item where itemPrice between 100 and 200") - .getResultList(); + final List expectedBetweenList = session.createQuery("From Item where itemPrice between 100 and 200").getResultList(); final String expectedPriceBetweenItems[] = new String[expectedBetweenList.size()]; for (int i = 0; i < expectedBetweenList.size(); i++) { expectedPriceBetweenItems[i] = expectedBetweenList.get(i).getItemName(); diff --git a/spring-hibernate5/src/test/java/com/baeldung/hibernate/criteria/HibernateCriteriaTestRunner.java b/spring-hibernate5/src/test/java/com/baeldung/hibernate/criteria/HibernateCriteriaTestRunner.java deleted file mode 100644 index 99164efb7a0f..000000000000 --- a/spring-hibernate5/src/test/java/com/baeldung/hibernate/criteria/HibernateCriteriaTestRunner.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.baeldung.hibernate.criteria; - -import org.junit.runner.JUnitCore; -import org.junit.runner.Result; -import org.junit.runner.notification.Failure; - -public class HibernateCriteriaTestRunner { - - public static void main(final String[] args) { - Result result = JUnitCore.runClasses(HibernateCriteriaTestSuite.class); - for (Failure failure : result.getFailures()) { - - } - } -} diff --git a/spring-hibernate5/src/test/java/com/baeldung/hibernate/criteria/HibernateCriteriaTestSuite.java b/spring-hibernate5/src/test/java/com/baeldung/hibernate/criteria/HibernateCriteriaTestSuite.java deleted file mode 100644 index dc1040734f9b..000000000000 --- a/spring-hibernate5/src/test/java/com/baeldung/hibernate/criteria/HibernateCriteriaTestSuite.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.baeldung.hibernate.criteria; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; - -@RunWith(Suite.class) -@Suite.SuiteClasses({ HibernateCriteriaIntegrationTest.class }) - -public class HibernateCriteriaTestSuite { - -} diff --git a/spring-hibernate5/src/test/java/com/baeldung/hibernate/immutable/HibernateImmutableIntegrationTest.java b/spring-hibernate5/src/test/java/com/baeldung/hibernate/immutable/HibernateImmutableIntegrationTest.java new file mode 100644 index 000000000000..801ddcdb45d3 --- /dev/null +++ b/spring-hibernate5/src/test/java/com/baeldung/hibernate/immutable/HibernateImmutableIntegrationTest.java @@ -0,0 +1,89 @@ +package com.baeldung.hibernate.immutable; + +import com.baeldung.hibernate.immutable.entities.Event; +import com.baeldung.hibernate.immutable.util.HibernateUtil; +import com.google.common.collect.Sets; +import org.hibernate.CacheMode; +import org.hibernate.Session; +import org.junit.*; +import org.junit.rules.ExpectedException; + +import javax.persistence.PersistenceException; + +public class HibernateImmutableIntegrationTest { + + private static Session session; + + @Rule + public final ExpectedException exception = ExpectedException.none(); + + @Before + public void before() { + session = HibernateUtil.getSessionFactory().getCurrentSession(); + session.beginTransaction(); + createEvent(); + session.setCacheMode(CacheMode.REFRESH); + } + + @BeforeClass + public static void setup() { + session = HibernateUtil.getSessionFactory().getCurrentSession(); + } + + @AfterClass + public static void teardown() { + HibernateUtil.getSessionFactory().close(); + } + + @Test + public void addEvent() { + Event event = new Event(); + event.setTitle("Public Event"); + session.save(event); + session.getTransaction().commit(); + } + + @Test + public void updateEvent() { + Event event = (Event) session.createQuery("FROM Event WHERE title='New Event'").list().get(0); + event.setTitle("Private Event"); + session.saveOrUpdate(event); + session.getTransaction().commit(); + } + + @Test + public void deleteEvent() { + Event event = (Event) session.createQuery("FROM Event WHERE title='New Event'").list().get(0); + session.delete(event); + session.getTransaction().commit(); + } + + @Test + public void addGuest() { + Event event = (Event) session.createQuery("FROM Event WHERE title='New Event'").list().get(0); + String newGuest = "Sara"; + event.getGuestList().add(newGuest); + + exception.expect(PersistenceException.class); + session.save(event); + session.getTransaction().commit(); + } + + @Test + public void deleteCascade() { + Event event = (Event) session.createQuery("FROM Event WHERE title='New Event'").list().get(0); + String guest = event.getGuestList().iterator().next(); + event.getGuestList().remove(guest); + + exception.expect(PersistenceException.class); + session.saveOrUpdate(event); + session.getTransaction().commit(); + } + + public static void createEvent() { + Event event = new Event(); + event.setTitle("New Event"); + event.setGuestList(Sets.newHashSet("guest")); + session.save(event); + } +} diff --git a/spring-hibernate5/src/test/java/com/baeldung/persistence/hibernate/FooPaginationPersistenceIntegrationTest.java b/spring-hibernate5/src/test/java/com/baeldung/persistence/hibernate/FooPaginationPersistenceIntegrationTest.java index c9152b4d8526..e6548ce5e7c1 100644 --- a/spring-hibernate5/src/test/java/com/baeldung/persistence/hibernate/FooPaginationPersistenceIntegrationTest.java +++ b/spring-hibernate5/src/test/java/com/baeldung/persistence/hibernate/FooPaginationPersistenceIntegrationTest.java @@ -73,8 +73,7 @@ public final void whenContextIsBootstrapped_thenNoExceptions() { public final void whenRetrievingPaginatedEntities_thenCorrectSize() { final int pageNumber = 1; final int pageSize = 10; - final List fooList = session.createQuery("From Foo").setFirstResult((pageNumber - 1) * pageSize) - .setMaxResults(pageSize).getResultList(); + final List fooList = session.createQuery("From Foo").setFirstResult((pageNumber - 1) * pageSize).setMaxResults(pageSize).getResultList(); assertThat(fooList, hasSize(pageSize)); } @@ -90,8 +89,7 @@ public final void whenRetrievingAllPages_thenCorrect() { final List fooList = Lists.newArrayList(); int totalEntities = 0; while (totalEntities < countResult) { - fooList.addAll(session.createQuery("From Foo").setFirstResult((pageNumber - 1) * pageSize) - .setMaxResults(pageSize).getResultList()); + fooList.addAll(session.createQuery("From Foo").setFirstResult((pageNumber - 1) * pageSize).setMaxResults(pageSize).getResultList()); totalEntities = fooList.size(); pageNumber++; } @@ -106,8 +104,7 @@ public final void whenRetrievingLastPage_thenCorrectSize() { final Long countResults = (Long) session.createQuery(countQ).uniqueResult(); final int lastPageNumber = (int) ((countResults / pageSize) + 1); - final List lastPage = session.createQuery("From Foo").setFirstResult((lastPageNumber - 1) * pageSize) - .setMaxResults(pageSize).getResultList(); + final List lastPage = session.createQuery("From Foo").setFirstResult((lastPageNumber - 1) * pageSize).setMaxResults(pageSize).getResultList(); assertThat(lastPage, hasSize(lessThan(pageSize + 1))); } @@ -147,8 +144,7 @@ public final void givenUsingTheCriteriaApi_whenRetrievingFirstPage_thenCorrect() CriteriaQuery criteriaItem = builder.createQuery(Foo.class); Root rootItem = criteriaItem.from(Foo.class); criteriaItem.select(rootItem); - final List firstPage = session.createQuery(criteriaItem).setFirstResult(0).setMaxResults(pageSize) - .getResultList(); + final List firstPage = session.createQuery(criteriaItem).setFirstResult(0).setMaxResults(pageSize).getResultList(); assertThat(firstPage, hasSize(pageSize)); } @@ -175,8 +171,7 @@ public final void givenUsingTheCriteriaApi_whenRetrievingPaginatedData_thenCorre int totalEntities = 0; while (totalEntities < count.intValue()) { - fooList.addAll(session.createQuery(criteriaFoo).setFirstResult((pageNumber - 1) * pageSize) - .setMaxResults(pageSize).getResultList()); + fooList.addAll(session.createQuery(criteriaFoo).setFirstResult((pageNumber - 1) * pageSize).setMaxResults(pageSize).getResultList()); totalEntities = fooList.size(); pageNumber++; } diff --git a/spring-hibernate5/src/test/java/com/baeldung/persistence/hibernate/FooSortingPersistenceIntegrationTest.java b/spring-hibernate5/src/test/java/com/baeldung/persistence/hibernate/FooSortingPersistenceIntegrationTest.java index 813fb6564105..9bd20e745c3d 100644 --- a/spring-hibernate5/src/test/java/com/baeldung/persistence/hibernate/FooSortingPersistenceIntegrationTest.java +++ b/spring-hibernate5/src/test/java/com/baeldung/persistence/hibernate/FooSortingPersistenceIntegrationTest.java @@ -11,8 +11,6 @@ import javax.persistence.criteria.Order; import javax.persistence.criteria.Root; -import org.hibernate.Criteria; -import org.hibernate.NullPrecedence; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.junit.After; @@ -151,7 +149,7 @@ public final void whenCriteriaSortingStringNullsLastAsc_thenNullsLast() { System.out.println("Id: " + foo.getId() + ", FirstName: " + foo.getName()); } } - + @Test public final void whenCriteriaSortingStringNullsFirstDesc_thenNullsFirst() { final Criteria criteria = session.createCriteria(Foo.class, "FOO"); diff --git a/spring-hibernate5/src/test/java/com/baeldung/persistence/save/SaveMethodsTest.java b/spring-hibernate5/src/test/java/com/baeldung/persistence/save/SaveMethodIntegrationTest.java similarity index 85% rename from spring-hibernate5/src/test/java/com/baeldung/persistence/save/SaveMethodsTest.java rename to spring-hibernate5/src/test/java/com/baeldung/persistence/save/SaveMethodIntegrationTest.java index b6cde868d34f..910cdc597f74 100644 --- a/spring-hibernate5/src/test/java/com/baeldung/persistence/save/SaveMethodsTest.java +++ b/spring-hibernate5/src/test/java/com/baeldung/persistence/save/SaveMethodIntegrationTest.java @@ -1,14 +1,6 @@ package com.baeldung.persistence.save; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; - -import javax.persistence.PersistenceException; - +import com.baeldung.persistence.model.Person; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.SessionFactory; @@ -17,19 +9,17 @@ import org.hibernate.cfg.Configuration; import org.hibernate.dialect.HSQLDialect; import org.hibernate.service.ServiceRegistry; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.*; -import com.baeldung.persistence.model.Person; +import javax.persistence.PersistenceException; + +import static org.junit.Assert.*; /** * Testing specific implementation details for different methods: persist, save, * merge, update, saveOrUpdate. */ -public class SaveMethodsTest { +public class SaveMethodIntegrationTest { private static SessionFactory sessionFactory; @@ -37,14 +27,9 @@ public class SaveMethodsTest { @BeforeClass public static void beforeTests() { - Configuration configuration = new Configuration().addAnnotatedClass(Person.class) - .setProperty("hibernate.dialect", HSQLDialect.class.getName()) - .setProperty("hibernate.connection.driver_class", org.hsqldb.jdbcDriver.class.getName()) - .setProperty("hibernate.connection.url", "jdbc:hsqldb:mem:test") - .setProperty("hibernate.connection.username", "sa").setProperty("hibernate.connection.password", "") - .setProperty("hibernate.hbm2ddl.auto", "update"); - ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings( - configuration.getProperties()).build(); + Configuration configuration = new Configuration().addAnnotatedClass(Person.class).setProperty("hibernate.dialect", HSQLDialect.class.getName()).setProperty("hibernate.connection.driver_class", org.hsqldb.jdbcDriver.class.getName()) + .setProperty("hibernate.connection.url", "jdbc:hsqldb:mem:test").setProperty("hibernate.connection.username", "sa").setProperty("hibernate.connection.password", "").setProperty("hibernate.hbm2ddl.auto", "update"); + ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build(); sessionFactory = configuration.buildSessionFactory(serviceRegistry); } @@ -269,10 +254,10 @@ public void whenSaveOrUpdatePersistent_thenNothingHappens() { @After public void tearDown() { - try{ - session.getTransaction().commit(); - session.close(); - }catch(TransactionException ex){ + try { + session.getTransaction().commit(); + session.close(); + } catch (TransactionException ex) { ex.printStackTrace(); } } diff --git a/spring-hibernate5/src/test/java/com/baeldung/persistence/service/FooStoredProceduresIntegrationTest.java b/spring-hibernate5/src/test/java/com/baeldung/persistence/service/FooStoredProceduresIntegrationTest.java index cfd3844079ee..8ab1b8680939 100644 --- a/spring-hibernate5/src/test/java/com/baeldung/persistence/service/FooStoredProceduresIntegrationTest.java +++ b/spring-hibernate5/src/test/java/com/baeldung/persistence/service/FooStoredProceduresIntegrationTest.java @@ -96,16 +96,14 @@ public final void getFoosByNameUsingStoredProcedures() { fooService.create(new Foo("NewFooName")); // Stored procedure getFoosByName using createSQLQuery() - List allFoosByName = session.createQuery("CALL GetFoosByName(:fooName)", Foo.class) - .setParameter("fooName", "NewFooName").getResultList(); + List allFoosByName = session.createQuery("CALL GetFoosByName(:fooName)", Foo.class).setParameter("fooName", "NewFooName").getResultList(); for (Foo foo : allFoosByName) { LOGGER.info("getFoosByName() using SQL Query : found => {}", foo.toString()); } // Stored procedure getFoosByName using getNamedQuery() @SuppressWarnings("unchecked") - List allFoosByName2 = session.getNamedQuery("callGetFoosByName").setParameter("fooName", "NewFooName") - .getResultList(); + List allFoosByName2 = session.getNamedQuery("callGetFoosByName").setParameter("fooName", "NewFooName").getResultList(); for (Foo foo : allFoosByName2) { LOGGER.info("getFoosByName() using Native Query : found => {}", foo.toString()); } diff --git a/spring-integration/pom.xml b/spring-integration/pom.xml index 0aa8fed598e1..34c40cb00f8d 100644 --- a/spring-integration/pom.xml +++ b/spring-integration/pom.xml @@ -16,7 +16,6 @@ 1.1.4.RELEASE 1.4.7 1.1.1 - 1.2.17 4.12 2.10 @@ -97,6 +96,12 @@ org.springframework.integration spring-integration-core ${spring.integration.version} + + + commons-logging + commons-logging + + javax.activation @@ -109,11 +114,6 @@ mail ${javax-mail.version} - - log4j - log4j - ${log4j.version} - org.springframework.integration spring-integration-twitter diff --git a/spring-integration/src/main/resources/log4j.xml b/spring-integration/src/main/resources/log4j.xml deleted file mode 100644 index cfa93a803730..000000000000 --- a/spring-integration/src/main/resources/log4j.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/spring-jersey/pom.xml b/spring-jersey/pom.xml index 41ebb9a6b53c..8d39da97333d 100644 --- a/spring-jersey/pom.xml +++ b/spring-jersey/pom.xml @@ -45,6 +45,8 @@ maven-surefire-plugin ${maven-surefire-plugin.version} + 3 + true **/*IntegrationTest.java **/*LiveTest.java @@ -132,6 +134,12 @@ httpclient ${httpclient.version} test + + + commons-logging + commons-logging + + diff --git a/spring-jersey/src/main/resources/logback.xml b/spring-jersey/src/main/resources/logback.xml index 788096686a8d..ec0dc2469ae0 100644 --- a/spring-jersey/src/main/resources/logback.xml +++ b/spring-jersey/src/main/resources/logback.xml @@ -1,15 +1,19 @@ + - - web - %date [%thread] %-5level %logger{36} - - %message%n + web - %date [%thread] %-5level %logger{36} - %message%n + + + + + + - \ No newline at end of file diff --git a/spring-jms/pom.xml b/spring-jms/pom.xml index de7c73431649..da71725d8f06 100644 --- a/spring-jms/pom.xml +++ b/spring-jms/pom.xml @@ -23,6 +23,12 @@ org.springframework spring-jms ${springframework.version} + + + commons-logging + commons-logging + + diff --git a/spring-jooq/pom.xml b/spring-jooq/pom.xml index 195c0b8514fa..4dce21116fa0 100644 --- a/spring-jooq/pom.xml +++ b/spring-jooq/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 com.baeldung - jooq-spring + sprin-jooq 0.0.1-SNAPSHOT @@ -35,6 +35,12 @@ org.springframework spring-context + + + commons-logging + commons-logging + + org.springframework diff --git a/spring-jpa/README.md b/spring-jpa/README.md index 313865e3f8f8..70f4404f9800 100644 --- a/spring-jpa/README.md +++ b/spring-jpa/README.md @@ -13,6 +13,7 @@ - [Hibernate Second-Level Cache](http://www.baeldung.com/hibernate-second-level-cache) - [Spring, Hibernate and a JNDI Datasource](http://www.baeldung.com/spring-persistence-jpa-jndi-datasource) - [Deleting Objects with Hibernate](http://www.baeldung.com/delete-with-hibernate) +- [Self-Contained Testing Using an In-Memory Database](http://www.baeldung.com/spring-jpa-test-in-memory-database) ### Eclipse Config After importing the project into Eclipse, you may see the following error: diff --git a/spring-jpa/pom.xml b/spring-jpa/pom.xml index 2229d64abee2..9ecbddb34d38 100644 --- a/spring-jpa/pom.xml +++ b/spring-jpa/pom.xml @@ -16,6 +16,12 @@ org.springframework spring-orm ${org.springframework.version} + + + commons-logging + commons-logging + + org.springframework @@ -196,14 +202,14 @@ - 4.3.4.RELEASE + 4.3.7.RELEASE 3.21.0-GA - 5.2.5.Final + 5.2.9.Final 5.1.40 - 1.10.5.RELEASE - 1.4.193 + 1.11.1.RELEASE + 1.4.194 1.2 diff --git a/spring-jpa/src/main/java/org/baeldung/config/StudentJpaConfig.java b/spring-jpa/src/main/java/org/baeldung/config/StudentJpaConfig.java new file mode 100644 index 000000000000..a40f180a6248 --- /dev/null +++ b/spring-jpa/src/main/java/org/baeldung/config/StudentJpaConfig.java @@ -0,0 +1,68 @@ +package org.baeldung.config; + +import java.util.Properties; + +import javax.persistence.EntityManagerFactory; +import javax.sql.DataSource; + +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.persistence.dao") +@PropertySource("persistence-student.properties") +@EnableTransactionManagement +public class StudentJpaConfig { + + @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.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/persistence/dao/StudentRepository.java b/spring-jpa/src/main/java/org/baeldung/persistence/dao/StudentRepository.java new file mode 100644 index 000000000000..af484b442c7e --- /dev/null +++ b/spring-jpa/src/main/java/org/baeldung/persistence/dao/StudentRepository.java @@ -0,0 +1,8 @@ +package org.baeldung.persistence.dao; + +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/persistence/model/Student.java new file mode 100644 index 000000000000..437eeac5bbda --- /dev/null +++ b/spring-jpa/src/main/java/org/baeldung/persistence/model/Student.java @@ -0,0 +1,38 @@ +package org.baeldung.persistence.model; + +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class Student { + + @Id + private long id; + private String name; + + public Student() { + } + + public Student(long id, String name) { + super(); + this.id = id; + this.name = name; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/spring-jpa/src/main/resources/logback.xml b/spring-jpa/src/main/resources/logback.xml index 1146dade632e..ec0dc2469ae0 100644 --- a/spring-jpa/src/main/resources/logback.xml +++ b/spring-jpa/src/main/resources/logback.xml @@ -1,20 +1,19 @@ + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - + + - - - - - - - - - + + + + + \ No newline at end of file diff --git a/spring-jpa/src/main/resources/persistence-student.properties b/spring-jpa/src/main/resources/persistence-student.properties new file mode 100644 index 000000000000..4e80b836d2f1 --- /dev/null +++ b/spring-jpa/src/main/resources/persistence-student.properties @@ -0,0 +1,11 @@ +jdbc.driverClassName=com.mysql.jdbc.Driver +jdbc.url=jdbc:mysql://localhost:3306/myDb +jdbc.user=tutorialuser +jdbc.pass=tutorialpass + +hibernate.dialect=org.hibernate.dialect.MySQL5Dialect +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/InMemoryDBTest.java b/spring-jpa/src/test/java/org/baeldung/persistence/repository/InMemoryDBTest.java new file mode 100644 index 000000000000..2c40c5b117fe --- /dev/null +++ b/spring-jpa/src/test/java/org/baeldung/persistence/repository/InMemoryDBTest.java @@ -0,0 +1,38 @@ +package org.baeldung.persistence.repository; + +import javax.annotation.Resource; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.AnnotationConfigContextLoader; +import org.springframework.transaction.annotation.Transactional; + +import org.baeldung.config.StudentJpaConfig; +import org.baeldung.persistence.model.Student; +import org.baeldung.persistence.dao.StudentRepository; + +import static org.junit.Assert.*; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = { StudentJpaConfig.class }, loader = AnnotationConfigContextLoader.class) +@Transactional +public class InMemoryDBTest { + + @Resource + private StudentRepository studentRepository; + + private static final long ID = 1; + private static final String NAME="john"; + + @Test + public void givenStudent_whenSave_thenGetOk(){ + Student student = new Student(ID, NAME); + studentRepository.save(student); + + Student student2 = studentRepository.findOne(ID); + assertEquals("name incorrect", NAME, student2.getName()); + } + +} diff --git a/spring-jpa/src/test/resources/persistence-student.properties b/spring-jpa/src/test/resources/persistence-student.properties new file mode 100644 index 000000000000..3b6b5806301e --- /dev/null +++ b/spring-jpa/src/test/resources/persistence-student.properties @@ -0,0 +1,9 @@ +jdbc.driverClassName=org.h2.Driver +jdbc.url=jdbc:h2:mem:myDb;DB_CLOSE_DELAY=-1 + +hibernate.dialect=org.hibernate.dialect.H2Dialect +hibernate.show_sql=true +hibernate.hbm2ddl.auto=create + +hibernate.cache.use_second_level_cache=false +hibernate.cache.use_query_cache=false \ No newline at end of file diff --git a/spring-kafka/README.md b/spring-kafka/README.md new file mode 100644 index 000000000000..291bbca6f615 --- /dev/null +++ b/spring-kafka/README.md @@ -0,0 +1,35 @@ +### Relevant articles + +- [Intro to Apache Kafka with Spring](http://www.baeldung.com/spring-kafka) + + + +# Spring Kafka + +This is a simple Spring Boot app to demonstrate sending and receiving of messages in Kafka using spring-kafka. + +As Kafka topics are not created automatically by default, this application requires that you create the following topics manually. + +`$ bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic baeldung`
+`$ bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 5 --topic partitioned`
+`$ bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic filtered`
+`$ bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic greeting`
+ +When the application runs successfully, following output is logged on to console (along with spring logs): + +#### Message received from the 'baeldung' topic by the basic listeners with groups foo and bar +>Received Message in group 'foo': Hello, World!
+Received Message in group 'bar': Hello, World! + +#### Message received from the 'baeldung' topic, with the partition info +>Received Message: Hello, World! from partition: 0 + +#### Message received from the 'partitioned' topic, only from specific partitions +>Received Message: Hello To Partioned Topic! from partition: 0
+Received Message: Hello To Partioned Topic! from partition: 3 + +#### Message received from the 'filtered' topic after filtering +>Received Message in filtered listener: Hello Baeldung! + +#### Message (Serialized Java Object) received from the 'greeting' topic +>Received greeting message: Greetings, World!! diff --git a/spring-kafka/pom.xml b/spring-kafka/pom.xml new file mode 100644 index 000000000000..11810a17ddfd --- /dev/null +++ b/spring-kafka/pom.xml @@ -0,0 +1,52 @@ + + 4.0.0 + + com.baeldung + spring-kafka + 0.0.1-SNAPSHOT + + spring-kafka + Intro to Kafka with Spring + + + 1.8 + 1.1.3.RELEASE + 2.6.7 + + + + org.springframework.boot + spring-boot-starter-parent + 1.5.2.RELEASE + + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.kafka + spring-kafka + + + + com.fasterxml.jackson.core + jackson-databind + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + \ No newline at end of file diff --git a/spring-kafka/src/main/java/com/baeldung/spring/kafka/Greeting.java b/spring-kafka/src/main/java/com/baeldung/spring/kafka/Greeting.java new file mode 100644 index 000000000000..b4633e802a1f --- /dev/null +++ b/spring-kafka/src/main/java/com/baeldung/spring/kafka/Greeting.java @@ -0,0 +1,37 @@ +package com.baeldung.spring.kafka; + +public class Greeting { + + private String msg; + private String name; + + public Greeting() { + + } + + public Greeting(String msg, String name) { + this.msg = msg; + this.name = name; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return msg + ", " + name + "!"; + } +} diff --git a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaApplication.java b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaApplication.java new file mode 100644 index 000000000000..50978d5ea980 --- /dev/null +++ b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaApplication.java @@ -0,0 +1,165 @@ +package com.baeldung.spring.kafka; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.kafka.annotation.TopicPartition; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.kafka.support.KafkaHeaders; +import org.springframework.messaging.handler.annotation.Header; +import org.springframework.messaging.handler.annotation.Payload; + +@SpringBootApplication +public class KafkaApplication { + + public static void main(String[] args) throws Exception { + + ConfigurableApplicationContext context = SpringApplication.run(KafkaApplication.class, args); + + MessageProducer producer = context.getBean(MessageProducer.class); + MessageListener listener = context.getBean(MessageListener.class); + /* + * Sending a Hello World message to topic 'baeldung'. + * Must be recieved by both listeners with group foo + * and bar with containerFactory fooKafkaListenerContainerFactory + * and barKafkaListenerContainerFactory respectively. + * It will also be recieved by the listener with + * headersKafkaListenerContainerFactory as container factory + */ + producer.sendMessage("Hello, World!"); + listener.latch.await(10, TimeUnit.SECONDS); + + /* + * Sending message to a topic with 5 partition, + * each message to a different partition. But as per + * listener configuration, only the messages from + * partition 0 and 3 will be consumed. + */ + for (int i = 0; i < 5; i++) { + producer.sendMessageToPartion("Hello To Partioned Topic!", i); + } + listener.partitionLatch.await(10, TimeUnit.SECONDS); + + /* + * Sending message to 'filtered' topic. As per listener + * configuration, all messages with char sequence + * 'World' will be discarded. + */ + producer.sendMessageToFiltered("Hello Baeldung!"); + producer.sendMessageToFiltered("Hello World!"); + listener.filterLatch.await(10, TimeUnit.SECONDS); + + /* + * Sending message to 'greeting' topic. This will send + * and recieved a java object with the help of + * greetingKafkaListenerContainerFactory. + */ + producer.sendGreetingMessage(new Greeting("Greetings", "World!")); + listener.greetingLatch.await(10, TimeUnit.SECONDS); + + context.close(); + } + + @Bean + public MessageProducer messageProducer() { + return new MessageProducer(); + } + + @Bean + public MessageListener messageListener() { + return new MessageListener(); + } + + public static class MessageProducer { + + @Autowired + private KafkaTemplate kafkaTemplate; + + @Autowired + private KafkaTemplate greetingKafkaTemplate; + + @Value(value = "${message.topic.name}") + private String topicName; + + @Value(value = "${partitioned.topic.name}") + private String partionedTopicName; + + @Value(value = "${filtered.topic.name}") + private String filteredTopicName; + + @Value(value = "${greeting.topic.name}") + private String greetingTopicName; + + public void sendMessage(String message) { + kafkaTemplate.send(topicName, message); + } + + public void sendMessageToPartion(String message, int partition) { + kafkaTemplate.send(partionedTopicName, partition, message); + } + + public void sendMessageToFiltered(String message) { + kafkaTemplate.send(filteredTopicName, message); + } + + public void sendGreetingMessage(Greeting greeting) { + greetingKafkaTemplate.send(greetingTopicName, greeting); + } + } + + public static class MessageListener { + + private CountDownLatch latch = new CountDownLatch(3); + + private CountDownLatch partitionLatch = new CountDownLatch(2); + + private CountDownLatch filterLatch = new CountDownLatch(2); + + private CountDownLatch greetingLatch = new CountDownLatch(1); + + @KafkaListener(topics = "${message.topic.name}", group = "foo", containerFactory = "fooKafkaListenerContainerFactory") + public void listenGroupFoo(String message) { + System.out.println("Received Messasge in group 'foo': " + message); + latch.countDown(); + } + + @KafkaListener(topics = "${message.topic.name}", group = "bar", containerFactory = "barKafkaListenerContainerFactory") + public void listenGroupBar(String message) { + System.out.println("Received Messasge in group 'bar': " + message); + latch.countDown(); + } + + @KafkaListener(topics = "${message.topic.name}", containerFactory = "headersKafkaListenerContainerFactory") + public void listenWithHeaders(@Payload String message, @Header(KafkaHeaders.RECEIVED_PARTITION_ID) int partition) { + System.out.println("Received Messasge: " + message + " from partition: " + partition); + latch.countDown(); + } + + @KafkaListener(topicPartitions = @TopicPartition(topic = "${partitioned.topic.name}", partitions = { "0", "3" })) + public void listenToParition(@Payload String message, @Header(KafkaHeaders.RECEIVED_PARTITION_ID) int partition) { + System.out.println("Received Message: " + message + " from partition: " + partition); + this.partitionLatch.countDown(); + } + + @KafkaListener(topics = "${filtered.topic.name}", containerFactory = "filterKafkaListenerContainerFactory") + public void listenWithFilter(String message) { + System.out.println("Recieved Message in filtered listener: " + message); + this.filterLatch.countDown(); + } + + @KafkaListener(topics = "${greeting.topic.name}", containerFactory = "greetingKafkaListenerContainerFactory") + public void greetingListener(Greeting greeting) { + System.out.println("Recieved greeting message: " + greeting); + this.greetingLatch.countDown(); + } + + } + +} diff --git a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaConsumerConfig.java b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaConsumerConfig.java new file mode 100644 index 000000000000..9353e63ff680 --- /dev/null +++ b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaConsumerConfig.java @@ -0,0 +1,84 @@ +package com.baeldung.spring.kafka; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.annotation.EnableKafka; +import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; +import org.springframework.kafka.core.ConsumerFactory; +import org.springframework.kafka.core.DefaultKafkaConsumerFactory; +import org.springframework.kafka.support.serializer.JsonDeserializer; + +@EnableKafka +@Configuration +public class KafkaConsumerConfig { + + @Value(value = "${kafka.bootstrapAddress}") + private String bootstrapAddress; + + public ConsumerFactory consumerFactory(String groupId) { + Map props = new HashMap<>(); + props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress); + props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId); + props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + return new DefaultKafkaConsumerFactory<>(props); + } + + @Bean + public ConcurrentKafkaListenerContainerFactory fooKafkaListenerContainerFactory() { + ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); + factory.setConsumerFactory(consumerFactory("foo")); + return factory; + } + + @Bean + public ConcurrentKafkaListenerContainerFactory barKafkaListenerContainerFactory() { + ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); + factory.setConsumerFactory(consumerFactory("bar")); + return factory; + } + + @Bean + public ConcurrentKafkaListenerContainerFactory headersKafkaListenerContainerFactory() { + ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); + factory.setConsumerFactory(consumerFactory("headers")); + return factory; + } + + @Bean + public ConcurrentKafkaListenerContainerFactory partitionsKafkaListenerContainerFactory() { + ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); + factory.setConsumerFactory(consumerFactory("partitions")); + return factory; + } + + @Bean + public ConcurrentKafkaListenerContainerFactory filterKafkaListenerContainerFactory() { + ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); + factory.setConsumerFactory(consumerFactory("filter")); + factory.setRecordFilterStrategy(record -> record.value() + .contains("World")); + return factory; + } + + public ConsumerFactory greetingConsumerFactory() { + Map props = new HashMap<>(); + props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress); + props.put(ConsumerConfig.GROUP_ID_CONFIG, "greeting"); + return new DefaultKafkaConsumerFactory<>(props, new StringDeserializer(), new JsonDeserializer<>(Greeting.class)); + } + + @Bean + public ConcurrentKafkaListenerContainerFactory greetingKafkaListenerContainerFactory() { + ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); + factory.setConsumerFactory(greetingConsumerFactory()); + return factory; + } + +} diff --git a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaProducerConfig.java b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaProducerConfig.java new file mode 100644 index 000000000000..7e2527b36e7a --- /dev/null +++ b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaProducerConfig.java @@ -0,0 +1,50 @@ +package com.baeldung.spring.kafka; + +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.common.serialization.StringSerializer; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.core.DefaultKafkaProducerFactory; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.kafka.core.ProducerFactory; +import org.springframework.kafka.support.serializer.JsonSerializer; + +import java.util.HashMap; +import java.util.Map; + +@Configuration +public class KafkaProducerConfig { + + @Value(value = "${kafka.bootstrapAddress}") + private String bootstrapAddress; + + @Bean + public ProducerFactory producerFactory() { + Map configProps = new HashMap<>(); + configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress); + configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + return new DefaultKafkaProducerFactory<>(configProps); + } + + @Bean + public KafkaTemplate kafkaTemplate() { + return new KafkaTemplate<>(producerFactory()); + } + + @Bean + public ProducerFactory greetingProducerFactory() { + Map configProps = new HashMap<>(); + configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress); + configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class); + return new DefaultKafkaProducerFactory<>(configProps); + } + + @Bean + public KafkaTemplate greetingKafkaTemplate() { + return new KafkaTemplate<>(greetingProducerFactory()); + } + +} diff --git a/spring-kafka/src/main/resources/application.properties b/spring-kafka/src/main/resources/application.properties new file mode 100644 index 000000000000..eaf113191ecf --- /dev/null +++ b/spring-kafka/src/main/resources/application.properties @@ -0,0 +1,5 @@ +kafka.bootstrapAddress=localhost:9092 +message.topic.name=baeldung +greeting.topic.name=greeting +filtered.topic.name=filtered +partitioned.topic.name=partitioned \ No newline at end of file diff --git a/spring-mvc-no-xml/.gitignore b/spring-ldap/.gitignore similarity index 100% rename from spring-mvc-no-xml/.gitignore rename to spring-ldap/.gitignore diff --git a/spring-ldap/README.md b/spring-ldap/README.md new file mode 100644 index 000000000000..f77572d982d7 --- /dev/null +++ b/spring-ldap/README.md @@ -0,0 +1,9 @@ +## Spring LDAP Example Project + +### Relevant articles + +- [Spring LDAP Overview](http://www.baeldung.com/spring-ldap) +- [Spring LDAP Example Project](http://www.baeldung.com/spring-ldap-overview/) + + + diff --git a/spring-ldap/pom.xml b/spring-ldap/pom.xml new file mode 100644 index 000000000000..123e72672a04 --- /dev/null +++ b/spring-ldap/pom.xml @@ -0,0 +1,187 @@ + + + 4.0.0 + + com.baeldung + spring-ldap + 0.1-SNAPSHOT + jar + + + 4.12 + 2.19.1 + 3.6.1 + 2.3.1.RELEASE + 1.7.22 + 1.1.8 + 4.3.6.RELEASE + 1.5.5 + 0.9.15 + 1.3 + + + + spring-ldap + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + **/*IntegrationTest.java + **/*LiveTest.java + + + + + + + + + + org.springframework.ldap + spring-ldap-core + ${spring-ldap.version} + + + commons-logging + commons-logging + + + + + + org.slf4j + jcl-over-slf4j + ${jcl.slf4j.version} + + + + ch.qos.logback + logback-classic + ${logback.version} + + + org.springframework + spring-context + ${spring-context.version} + + + + + + + junit + junit + ${junit.version} + test + + + + org.hamcrest + hamcrest-core + ${org.hamcrest.version} + test + + + org.hamcrest + hamcrest-library + ${org.hamcrest.version} + test + + + + + org.springframework.ldap + spring-ldap-test + ${spring-ldap.version} + test + + + commons-logging + commons-logging + + + + + + + org.apache.directory.server + apacheds-core + ${apacheds.version} + test + + + org.apache.directory.server + apacheds-core-entry + ${apacheds.version} + test + + + org.apache.directory.server + apacheds-protocol-shared + ${apacheds.version} + test + + + org.apache.directory.server + apacheds-protocol-ldap + ${apacheds.version} + test + + + org.apache.directory.server + apacheds-server-jndi + ${apacheds.version} + test + + + org.apache.directory.shared + shared-ldap + ${shared-ldap.version} + test + + + + + + + live + + + + org.apache.maven.plugins + maven-surefire-plugin + + + integration-test + + test + + + + **/*IntegrationTest.java + + + **/*LiveTest.java + + + + + + + + + + \ No newline at end of file diff --git a/spring-ldap/src/main/java/com/baeldung/ldap/client/LdapClient.java b/spring-ldap/src/main/java/com/baeldung/ldap/client/LdapClient.java new file mode 100644 index 000000000000..0c77d0f87d90 --- /dev/null +++ b/spring-ldap/src/main/java/com/baeldung/ldap/client/LdapClient.java @@ -0,0 +1,83 @@ +package com.baeldung.ldap.client; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.ldap.core.*; +import org.springframework.ldap.support.LdapNameBuilder; + +import javax.naming.Name; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; +import java.util.List; + +public class LdapClient { + + @Autowired + private Environment env; + + @Autowired + private ContextSource contextSource; + + @Autowired + private LdapTemplate ldapTemplate; + + public void authenticate(final String username, final String password) { + contextSource.getContext("cn=" + username + ",ou=users," + env.getRequiredProperty("ldap.partitionSuffix"), password); + } + + public List search(final String username) { + return ldapTemplate.search( + "ou=users", + "cn=" + username, + (AttributesMapper) attrs -> (String) attrs + .get("cn") + .get()); + } + + public void create(final String username, final String password) { + Name dn = LdapNameBuilder + .newInstance() + .add("ou", "users") + .add("cn", username) + .build(); + DirContextAdapter context = new DirContextAdapter(dn); + + context.setAttributeValues("objectclass", new String[] { "top", "person", "organizationalPerson", "inetOrgPerson" }); + context.setAttributeValue("cn", username); + context.setAttributeValue("sn", username); + context.setAttributeValue("userPassword", digestSHA(password)); + + ldapTemplate.bind(context); + } + + public void modify(final String username, final String password) { + Name dn = LdapNameBuilder + .newInstance() + .add("ou", "users") + .add("cn", username) + .build(); + DirContextOperations context = ldapTemplate.lookupContext(dn); + + context.setAttributeValues("objectclass", new String[] { "top", "person", "organizationalPerson", "inetOrgPerson" }); + context.setAttributeValue("cn", username); + context.setAttributeValue("sn", username); + context.setAttributeValue("userPassword", digestSHA(password)); + + ldapTemplate.modifyAttributes(context); + } + + private String digestSHA(final String password) { + String base64; + try { + MessageDigest digest = MessageDigest.getInstance("SHA"); + digest.update(password.getBytes()); + base64 = Base64 + .getEncoder() + .encodeToString(digest.digest()); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + return "{SHA}" + base64; + } +} diff --git a/spring-ldap/src/main/java/com/baeldung/ldap/javaconfig/AppConfig.java b/spring-ldap/src/main/java/com/baeldung/ldap/javaconfig/AppConfig.java new file mode 100644 index 000000000000..8572e5d1be61 --- /dev/null +++ b/spring-ldap/src/main/java/com/baeldung/ldap/javaconfig/AppConfig.java @@ -0,0 +1,44 @@ +package com.baeldung.ldap.javaconfig; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.context.annotation.PropertySource; +import org.springframework.core.env.Environment; +import org.springframework.ldap.core.LdapTemplate; +import org.springframework.ldap.core.support.LdapContextSource; + +import com.baeldung.ldap.client.LdapClient; + +@Configuration +@PropertySource("classpath:application.properties") +@ComponentScan(basePackages = { "com.baeldung.ldap.*" }) +@Profile("default") +public class AppConfig { + + @Autowired + private Environment env; + + @Bean + public LdapContextSource contextSource() { + LdapContextSource contextSource = new LdapContextSource(); + contextSource.setUrl(env.getRequiredProperty("ldap.url")); + contextSource.setBase(env.getRequiredProperty("ldap.partitionSuffix")); + contextSource.setUserDn(env.getRequiredProperty("ldap.principal")); + contextSource.setPassword(env.getRequiredProperty("ldap.password")); + return contextSource; + } + + @Bean + public LdapTemplate ldapTemplate() { + return new LdapTemplate(contextSource()); + } + + @Bean + public LdapClient ldapClient() { + return new LdapClient(); + } + +} diff --git a/spring-ldap/src/main/resources/application.properties b/spring-ldap/src/main/resources/application.properties new file mode 100644 index 000000000000..670fb792347c --- /dev/null +++ b/spring-ldap/src/main/resources/application.properties @@ -0,0 +1,6 @@ +ldap.partitionSuffix=dc=example,dc=com +ldap.partition=example +ldap.principal=uid=admin,ou=system +ldap.password=secret +ldap.port=18889 +ldap.url=ldap://localhost:18889 \ No newline at end of file diff --git a/spring-ldap/src/main/resources/logback.xml b/spring-ldap/src/main/resources/logback.xml new file mode 100644 index 000000000000..ec0dc2469ae0 --- /dev/null +++ b/spring-ldap/src/main/resources/logback.xml @@ -0,0 +1,19 @@ + + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-ldap/src/test/java/com/baeldung/ldap/client/LdapClientLiveTest.java b/spring-ldap/src/test/java/com/baeldung/ldap/client/LdapClientLiveTest.java new file mode 100644 index 000000000000..b65588dc38f7 --- /dev/null +++ b/spring-ldap/src/test/java/com/baeldung/ldap/client/LdapClientLiveTest.java @@ -0,0 +1,63 @@ +package com.baeldung.ldap.client; + +import java.util.List; + +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.ldap.AuthenticationException; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.AnnotationConfigContextLoader; + +import com.baeldung.ldap.javaconfig.TestConfig; + +@RunWith(SpringJUnit4ClassRunner.class) +@ActiveProfiles("testlive") +@ContextConfiguration(classes = { TestConfig.class }, loader = AnnotationConfigContextLoader.class) +public class LdapClientLiveTest { + + private static final String USER2 = "TEST02"; + private static final String USER3 = "TEST03"; + private static final String USER4 = "TEST04"; + + private static final String USER2_PWD = "TEST02"; + private static final String USER3_PWD = "TEST03"; + private static final String USER4_PWD = "TEST04"; + + private static final String SEARCH_STRING = "TEST*"; + + @Autowired + private LdapClient ldapClient; + + @Test + public void givenLdapClient_whenCorrectCredentials_thenSuccessfulLogin() { + ldapClient.authenticate(USER3, USER3_PWD); + } + + @Test(expected = AuthenticationException.class) + public void givenLdapClient_whenIncorrectCredentials_thenFailedLogin() { + ldapClient.authenticate(USER3, USER2_PWD); + } + + @Test + public void givenLdapClient_whenCorrectSearchFilter_thenEntriesReturned() { + List users = ldapClient.search(SEARCH_STRING); + Assert.assertThat(users, Matchers.containsInAnyOrder(USER2, USER3)); + } + + @Test + public void givenLdapClientNotExists_whenDataProvided_thenNewUserCreated() { + ldapClient.create(USER4, USER4_PWD); + ldapClient.authenticate(USER4, USER4_PWD); + } + + @Test + public void givenLdapClientExists_whenDataProvided_thenExistingUserModified() { + ldapClient.modify(USER2, USER3_PWD); + ldapClient.authenticate(USER2, USER3_PWD); + } +} diff --git a/spring-ldap/src/test/java/com/baeldung/ldap/javaconfig/TestConfig.java b/spring-ldap/src/test/java/com/baeldung/ldap/javaconfig/TestConfig.java new file mode 100644 index 000000000000..e2968e977c82 --- /dev/null +++ b/spring-ldap/src/test/java/com/baeldung/ldap/javaconfig/TestConfig.java @@ -0,0 +1,59 @@ +package com.baeldung.ldap.javaconfig; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.context.annotation.PropertySource; +import org.springframework.core.env.Environment; +import org.springframework.core.io.ResourceLoader; +import org.springframework.ldap.core.LdapTemplate; +import org.springframework.ldap.core.support.LdapContextSource; +import org.springframework.ldap.test.TestContextSourceFactoryBean; + +import com.baeldung.ldap.client.LdapClient; + +@Configuration +@PropertySource("classpath:test_application.properties") +@ComponentScan(basePackages = { "com.baeldung.ldap.*" }) +@Profile("testlive") +public class TestConfig { + @Autowired + private Environment env; + + @Autowired + private ResourceLoader resourceLoader; + + @Bean + public TestContextSourceFactoryBean testContextSource() { + TestContextSourceFactoryBean contextSource = new TestContextSourceFactoryBean(); + contextSource.setDefaultPartitionName(env.getRequiredProperty("ldap.partition")); + contextSource.setDefaultPartitionSuffix(env.getRequiredProperty("ldap.partitionSuffix")); + contextSource.setPrincipal(env.getRequiredProperty("ldap.principal")); + contextSource.setPassword(env.getRequiredProperty("ldap.password")); + contextSource.setLdifFile(resourceLoader.getResource(env.getRequiredProperty("ldap.ldiffile"))); + contextSource.setPort(Integer.valueOf(env.getRequiredProperty("ldap.port"))); + return contextSource; + } + + @Bean + public LdapContextSource contextSource() { + LdapContextSource contextSource = new LdapContextSource(); + contextSource.setUrl(env.getRequiredProperty("ldap.url")); + contextSource.setBase(env.getRequiredProperty("ldap.partitionSuffix")); + contextSource.setUserDn(env.getRequiredProperty("ldap.principal")); + contextSource.setPassword(env.getRequiredProperty("ldap.password")); + return contextSource; + } + + @Bean + public LdapTemplate ldapTemplate() { + return new LdapTemplate(contextSource()); + } + + @Bean + public LdapClient ldapClient() { + return new LdapClient(); + } +} diff --git a/spring-ldap/src/test/resources/test.ldif b/spring-ldap/src/test/resources/test.ldif new file mode 100644 index 000000000000..234463020460 --- /dev/null +++ b/spring-ldap/src/test/resources/test.ldif @@ -0,0 +1,24 @@ +version: 1 +dn: ou=users,dc=example,dc=com +objectClass: organizationalUnit +objectClass: top +ou: users + +dn: cn=TEST03,ou=users,dc=example,dc=com +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +objectClass: top +cn: TEST03 +sn: TEST03 +userPassword:: e1NIQX1JbktFOFY2enBpWWdMY0RYQTYzdXZVNjRGZXc9 + +dn: cn=TEST02,ou=users,dc=example,dc=com +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +objectClass: top +cn: TEST02 +sn: TEST02 +userPassword:: e1NIQX1uZERKdWNNYnl5a3hWdEkyQzgyRUFlalN1WTQ9 + diff --git a/spring-ldap/src/test/resources/test_application.properties b/spring-ldap/src/test/resources/test_application.properties new file mode 100644 index 000000000000..40b90cfdcfe1 --- /dev/null +++ b/spring-ldap/src/test/resources/test_application.properties @@ -0,0 +1,7 @@ +ldap.partitionSuffix=dc=example,dc=com +ldap.partition=example +ldap.principal=uid=admin,ou=system +ldap.password=secret +ldap.ldiffile=classpath:/test.ldif +ldap.port=18888 +ldap.url=ldap://localhost:18888 \ No newline at end of file diff --git a/spring-mvc-forms/README.md b/spring-mvc-forms/README.md index 51dbef98564b..86abd7e4c120 100644 --- a/spring-mvc-forms/README.md +++ b/spring-mvc-forms/README.md @@ -2,3 +2,5 @@ ### Relevant Articles - [MaxUploadSizeExceededException in Spring](http://www.baeldung.com/spring-maxuploadsizeexceeded) +- [Getting Started with Forms in Spring MVC](http://www.baeldung.com/spring-mvc-form-tutorial) +- [Form Validation with AngularJS and Spring MVC](http://www.baeldung.com/validation-angularjs-spring-mvc) diff --git a/spring-mvc-forms/pom.xml b/spring-mvc-forms/pom.xml index 35ed00c0e935..12b0ba7e7cb5 100644 --- a/spring-mvc-forms/pom.xml +++ b/spring-mvc-forms/pom.xml @@ -15,6 +15,12 @@ org.springframework spring-webmvc ${springframework.version} + + + commons-logging + commons-logging + +
@@ -46,6 +52,12 @@ commons-fileupload ${fileupload.version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + @@ -89,16 +101,17 @@ - 4.3.4.RELEASE + 4.3.7.RELEASE 2.6 1.2 2.3.1 3.1.0 3.6.0 1.8 - 5.3.3.Final + 5.4.0.Final enter-location-of-server 1.3.2 + 2.8.7 diff --git a/spring-mvc-forms/src/main/java/com/baeldung/springmvcforms/configuration/ApplicationConfiguration.java b/spring-mvc-forms/src/main/java/com/baeldung/springmvcforms/configuration/ApplicationConfiguration.java index 3f7889422fe0..7292d95b212f 100644 --- a/spring-mvc-forms/src/main/java/com/baeldung/springmvcforms/configuration/ApplicationConfiguration.java +++ b/spring-mvc-forms/src/main/java/com/baeldung/springmvcforms/configuration/ApplicationConfiguration.java @@ -27,6 +27,16 @@ public InternalResourceViewResolver jspViewResolver() { bean.setSuffix(".jsp"); return bean; } + + + @Bean + public InternalResourceViewResolver htmlViewResolver() { + InternalResourceViewResolver bean = new InternalResourceViewResolver(); + bean.setPrefix("/WEB-INF/html/"); + bean.setSuffix(".html"); + bean.setOrder(2); + return bean; + } @Bean public MultipartResolver multipartResolver() { diff --git a/spring-mvc-forms/src/main/java/com/baeldung/springmvcforms/controller/UserController.java b/spring-mvc-forms/src/main/java/com/baeldung/springmvcforms/controller/UserController.java new file mode 100644 index 000000000000..f5553cf2b758 --- /dev/null +++ b/spring-mvc-forms/src/main/java/com/baeldung/springmvcforms/controller/UserController.java @@ -0,0 +1,59 @@ +package com.baeldung.springmvcforms.controller; + +import com.baeldung.springmvcforms.domain.User; +import org.springframework.context.support.DefaultMessageSourceResolvable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.validation.Valid; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +@Controller +public class UserController { + + private List users = Arrays.asList( + new User("ana@yahoo.com", "pass", "Ana", 20), + new User("bob@yahoo.com", "pass", "Bob", 30), + new User("john@yahoo.com", "pass", "John", 40), + new User("mary@yahoo.com", "pass", "Mary", 30)); + + @GetMapping("/userPage") + public String getUserProfilePage() { + return "user"; + } + + @PostMapping("/user") + @ResponseBody + public ResponseEntity saveUser(@Valid User user, BindingResult result, Model model) { + if (result.hasErrors()) { + final List errors = result.getAllErrors().stream() + .map(DefaultMessageSourceResolvable::getDefaultMessage) + .collect(Collectors.toList()); + + return new ResponseEntity<>(errors, HttpStatus.OK); + } else { + if (users.stream().anyMatch(it -> user.getEmail().equals(it.getEmail()))) { + return new ResponseEntity<>(Collections.singletonList("Email already exists!"), HttpStatus.CONFLICT); + } else { + users.add(user); + return new ResponseEntity<>(HttpStatus.CREATED); + } + } + } + + @GetMapping("/users") + @ResponseBody + public List getUsers() { + return users; + } + +} diff --git a/spring-mvc-forms/src/main/java/com/baeldung/springmvcforms/domain/User.java b/spring-mvc-forms/src/main/java/com/baeldung/springmvcforms/domain/User.java new file mode 100644 index 000000000000..49f006f4220e --- /dev/null +++ b/spring-mvc-forms/src/main/java/com/baeldung/springmvcforms/domain/User.java @@ -0,0 +1,71 @@ +package com.baeldung.springmvcforms.domain; + +import javax.validation.constraints.Digits; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +import org.hibernate.validator.constraints.Email; +import org.hibernate.validator.constraints.NotBlank; + +public class User { + + @NotNull + @Email + private String email; + + @NotNull + @Size(min = 4, max = 15) + private String password; + + @NotBlank + private String name; + + @Min(18) + @Digits(integer = 2, fraction = 0) + private int age; + + public User() { + + } + + public User(String email, String password, String name, int age) { + super(); + this.email = email; + this.password = password; + this.name = name; + this.age = age; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +} diff --git a/spring-mvc-forms/src/main/webapp/WEB-INF/html/user.html b/spring-mvc-forms/src/main/webapp/WEB-INF/html/user.html new file mode 100644 index 000000000000..a505f51f3206 --- /dev/null +++ b/spring-mvc-forms/src/main/webapp/WEB-INF/html/user.html @@ -0,0 +1,68 @@ + + + + +Users + + + + + + + + +
+ + + +
+

Invalid email!

+

Email is required!

+
+
+ + + +
+

Password must be at least 4 characters!

+

Password must not be longer than 15 characters!

+

Password is required!

+
+
+ + + +
+

Name is required!

+
+
+ + + +
+

Age must be greater than 18!

+

Age must be greater than 18!

+
+
+
+ + +
+ +
+
{{errorMessage}}
+
{{message}}
+
+ +All Users:
+ + + + + + + +
EmailNameAge
{{usr.email}} {{usr.name}} {{usr.age}}
+ + \ No newline at end of file diff --git a/spring-mvc-forms/src/main/webapp/css/user.css b/spring-mvc-forms/src/main/webapp/css/user.css new file mode 100644 index 000000000000..bcfb62a33cd6 --- /dev/null +++ b/spring-mvc-forms/src/main/webapp/css/user.css @@ -0,0 +1,77 @@ +.form-label { + display:block; + margin-top:16px; + font-weight:bold; +} + +.form-input { + border-radius:5px; + display:inline; +} + +.form-input p { + margin:0; +} + +.form-button { + margin:5px; +} + +.form-error input.ng-invalid { + border-color:red; +} + +.check { + display:inline; + color:green; + font-weight:bold; +} + +.error-messages { + color:red; +} + +.error-messages p { + margin:0; +} + +#customers { + font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; + border-collapse: collapse; + width: 100%; +} + +#users td, #users th { + border: 1px solid dodgerblue; + border-collapse:collapse; + padding: 4px; + width:200px; +} + +#users tr:nth-child(even){background-color: lavender;} + +#users th { + padding-top: 10px; + padding-bottom: 10px; + text-align: left; + background-color: dodgerblue; +} + +#users { + border-collapse:collapse; +} + +button { + border-radius:5px; + cursor:pointer; + margin:10px; + padding:5px; +} + +.form-button-save { + background-color:lightgreen; +} + +.form-button-reset { + background-color:lightpink; +} \ No newline at end of file diff --git a/spring-mvc-forms/src/main/webapp/js/app.js b/spring-mvc-forms/src/main/webapp/js/app.js new file mode 100644 index 000000000000..6a290b3cf0a5 --- /dev/null +++ b/spring-mvc-forms/src/main/webapp/js/app.js @@ -0,0 +1,70 @@ +var app = angular.module('app', ['ngMessages']); + +app.controller('UserCtrl', ['$scope','UserService', function ($scope,UserService) { + + $scope.submitted = false; + + $scope.getUsers = function() { + UserService.getUsers().then( function(data){ + $scope.users = data; + }); + } + + $scope.saveUser = function () { + $scope.submitted = true; + if ($scope.userForm.$valid){ + UserService.saveUser($scope.user) + .then (function success(response){ + $scope.message = 'User added!'; + $scope.errorMessage = ''; + $scope.getUsers(); + $scope.user = null; + $scope.submitted = false; + }, + function error(response){ + if (response.status == 409){ + $scope.errorMessage = response.data[0]; + } + else { + $scope.errorMessage = 'Error adding user!'; + } + $scope.message = ''; + }); + } + } + + $scope.getUsers(); + + $scope.resetForm = function () { + $scope.userForm.$setPristine(); + $scope.user=null; + $scope.message=''; + $scope.errorMessage=''; + $scope.submitted = false; + } + +}]); + +app.service('UserService',['$http', function ($http) { + + this.saveUser = function saveUser(user){ + return $http({ + method: 'POST', + url: 'user', + params: {email:user.email, password:user.password, name:user.name, age:user.age}, + headers: 'Accept:application/json' + }); + } + + + this.getUsers = function getUsers(){ + return $http({ + method: 'GET', + url: 'users', + headers:'Accept:application/json' + }).then( function(response){ + return response.data; + } ); + } + +}]); \ No newline at end of file diff --git a/spring-mvc-java/README.md b/spring-mvc-java/README.md index 4d3e58558bad..533d37fe00a6 100644 --- a/spring-mvc-java/README.md +++ b/spring-mvc-java/README.md @@ -21,3 +21,4 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring - [Spring @RequestMapping New Shortcut Annotations](http://www.baeldung.com/spring-new-requestmapping-shortcuts) - [Guide to Spring Handler Mappings](http://www.baeldung.com/spring-handler-mappings) - [Uploading and Displaying Excel Files with Spring MVC](http://www.baeldung.com/spring-mvc-excel-files) +- [Spring MVC Custom Validation](http://www.baeldung.com/spring-mvc-custom-validator) diff --git a/spring-mvc-java/pom.xml b/spring-mvc-java/pom.xml index ef18cef3e0ea..f47b723beb3d 100644 --- a/spring-mvc-java/pom.xml +++ b/spring-mvc-java/pom.xml @@ -84,6 +84,12 @@ net.sourceforge.htmlunit htmlunit ${net.sourceforge.htmlunit} + + + commons-logging + commons-logging + + @@ -116,11 +122,6 @@ ${org.slf4j.version} - - org.slf4j - log4j-over-slf4j - ${org.slf4j.version} - @@ -166,7 +167,28 @@ poi-ooxml ${poi.version} - + + + + javax.validation + validation-api + 1.1.0.Final + + + org.hibernate + hibernate-validator + ${hibernate-validator.version} + + + javax.el + javax.el-api + 2.2.4 + + + org.glassfish.web + javax.el + 2.2.4 + @@ -349,7 +371,7 @@ 1.1.7 - 5.3.3.Final + 5.4.1.Final 3.1.0 1.2 diff --git a/spring-mvc-java/src/main/java/com/baeldung/customvalidator/ContactNumberConstraint.java b/spring-mvc-java/src/main/java/com/baeldung/customvalidator/ContactNumberConstraint.java new file mode 100644 index 000000000000..dbd38c11226b --- /dev/null +++ b/spring-mvc-java/src/main/java/com/baeldung/customvalidator/ContactNumberConstraint.java @@ -0,0 +1,24 @@ +package com.baeldung.customvalidator; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import javax.validation.Constraint; +import javax.validation.Payload; + +@Documented +@Constraint(validatedBy = ContactNumberValidator.class) +@Target({ElementType.METHOD, ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ContactNumberConstraint { + + String message() default "Invalid phone number"; + + Class[] groups() default {}; + + Class[] payload() default {}; + +} diff --git a/spring-mvc-java/src/main/java/com/baeldung/customvalidator/ContactNumberValidator.java b/spring-mvc-java/src/main/java/com/baeldung/customvalidator/ContactNumberValidator.java new file mode 100644 index 000000000000..dea6b9099bf6 --- /dev/null +++ b/spring-mvc-java/src/main/java/com/baeldung/customvalidator/ContactNumberValidator.java @@ -0,0 +1,17 @@ +package com.baeldung.customvalidator; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +public class ContactNumberValidator implements ConstraintValidator { + + @Override + public void initialize(ContactNumberConstraint contactNumber) { + } + + @Override + public boolean isValid(String contactField, ConstraintValidatorContext cxt) { + return contactField != null && contactField.matches("[0-9]+") && (contactField.length() > 8) && (contactField.length() < 14); + } + +} diff --git a/spring-mvc-java/src/main/java/com/baeldung/model/ValidatedPhone.java b/spring-mvc-java/src/main/java/com/baeldung/model/ValidatedPhone.java new file mode 100644 index 000000000000..be702a85172e --- /dev/null +++ b/spring-mvc-java/src/main/java/com/baeldung/model/ValidatedPhone.java @@ -0,0 +1,22 @@ +package com.baeldung.model; + +import com.baeldung.customvalidator.ContactNumberConstraint; + +public class ValidatedPhone { + + @ContactNumberConstraint + private String phone; + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + @Override + public String toString() { + return phone; + } +} diff --git a/spring-mvc-java/src/main/java/com/baeldung/web/controller/UserController.java b/spring-mvc-java/src/main/java/com/baeldung/web/controller/UserController.java index fda159f20484..e72ec06830eb 100644 --- a/spring-mvc-java/src/main/java/com/baeldung/web/controller/UserController.java +++ b/spring-mvc-java/src/main/java/com/baeldung/web/controller/UserController.java @@ -3,15 +3,16 @@ import com.baeldung.model.User; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; @Controller @RequestMapping("/") public class UserController { - @RequestMapping(value = "/", method = RequestMethod.GET) + @GetMapping("/") public String showForm(final Model model) { final User user = new User(); user.setFirstname("John"); @@ -21,7 +22,7 @@ public String showForm(final Model model) { return "index"; } - @RequestMapping(value = "/processForm", method = RequestMethod.POST) + @PostMapping("/processForm") public String processForm(@ModelAttribute(value = "user") final User user, final Model model) { // Insert User into DB model.addAttribute("name", user.getFirstname() + " " + user.getLastname()); diff --git a/spring-mvc-java/src/main/java/com/baeldung/web/controller/ValidatedPhoneController.java b/spring-mvc-java/src/main/java/com/baeldung/web/controller/ValidatedPhoneController.java new file mode 100644 index 000000000000..8c6cfcd3be3b --- /dev/null +++ b/spring-mvc-java/src/main/java/com/baeldung/web/controller/ValidatedPhoneController.java @@ -0,0 +1,34 @@ +package com.baeldung.web.controller; + +import com.baeldung.model.ValidatedPhone; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +import javax.validation.Valid; + +@Controller +@EnableWebMvc +public class ValidatedPhoneController { + + @GetMapping("/validatePhone") + public String loadFormPage(Model m) { + m.addAttribute("validatedPhone", new ValidatedPhone()); + return "phoneHome"; + } + + @PostMapping("/addValidatePhone") + public String submitForm(@Valid ValidatedPhone validatedPhone, BindingResult result, Model m) { + if (result.hasErrors()) { + return "phoneHome"; + } + + m.addAttribute("message", "Successfully saved phone: " + validatedPhone.toString()); + return "phoneHome"; + } + + +} diff --git a/spring-mvc-java/src/main/resources/logback.xml b/spring-mvc-java/src/main/resources/logback.xml index e0721aa89087..ec0dc2469ae0 100644 --- a/spring-mvc-java/src/main/resources/logback.xml +++ b/spring-mvc-java/src/main/resources/logback.xml @@ -1,21 +1,19 @@ + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - - - %date [%thread] %-5level %logger{6} - %message%n - - - + + - - - - - - - - - + + + + + \ No newline at end of file diff --git a/spring-mvc-java/src/main/webapp/WEB-INF/mvc-servlet.xml b/spring-mvc-java/src/main/webapp/WEB-INF/mvc-servlet.xml index 4ba96424480e..b0a4d4892a3a 100644 --- a/spring-mvc-java/src/main/webapp/WEB-INF/mvc-servlet.xml +++ b/spring-mvc-java/src/main/webapp/WEB-INF/mvc-servlet.xml @@ -1,6 +1,14 @@ - - + + + \ No newline at end of file diff --git a/spring-mvc-java/src/main/webapp/WEB-INF/view/phoneHome.jsp b/spring-mvc-java/src/main/webapp/WEB-INF/view/phoneHome.jsp new file mode 100644 index 000000000000..b873e9bc5f42 --- /dev/null +++ b/spring-mvc-java/src/main/webapp/WEB-INF/view/phoneHome.jsp @@ -0,0 +1,38 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> + + + + + Sample Form + + + + +
+ +

Phone Number

+
${message}
+ + + + + + +
+ + +
+
+ + diff --git a/spring-mvc-java/src/test/java/com/baeldung/web/controller/CustomMVCValidatorTest.java b/spring-mvc-java/src/test/java/com/baeldung/web/controller/CustomMVCValidatorTest.java new file mode 100644 index 000000000000..069cc8e141e0 --- /dev/null +++ b/spring-mvc-java/src/test/java/com/baeldung/web/controller/CustomMVCValidatorTest.java @@ -0,0 +1,41 @@ +package com.baeldung.web.controller; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +public class CustomMVCValidatorTest { + + private MockMvc mockMvc; + + @Before + public void setup(){ + this.mockMvc = MockMvcBuilders.standaloneSetup(new ValidatedPhoneController()).build(); + } + + @Test + public void givenPhonePageUri_whenMockMvc_thenReturnsPhonePage() throws Exception{ + this.mockMvc.perform(get("/validatePhone")).andExpect(view().name("phoneHome")); + } + + @Test + public void givenPhoneURIWithPostAndFormData_whenMockMVC_thenVerifyErrorResponse() throws Exception { + this.mockMvc.perform(MockMvcRequestBuilders.post("/addValidatePhone"). + accept(MediaType.TEXT_HTML). + param("phoneInput", "123")). + andExpect(model().attributeHasFieldErrorCode("validatedPhone", "phone","ContactNumberConstraint")). + andExpect(view().name("phoneHome")). + andExpect(status().isOk()). + andDo(print()); + } + +} diff --git a/spring-mvc-java/src/test/java/com/baeldung/web/controller/EmployeeTestWithoutMockMvc.java b/spring-mvc-java/src/test/java/com/baeldung/web/controller/EmployeeIntegrationTest.java similarity index 97% rename from spring-mvc-java/src/test/java/com/baeldung/web/controller/EmployeeTestWithoutMockMvc.java rename to spring-mvc-java/src/test/java/com/baeldung/web/controller/EmployeeIntegrationTest.java index 19806e055909..0c2aa3de1b0a 100644 --- a/spring-mvc-java/src/test/java/com/baeldung/web/controller/EmployeeTestWithoutMockMvc.java +++ b/spring-mvc-java/src/test/java/com/baeldung/web/controller/EmployeeIntegrationTest.java @@ -15,7 +15,7 @@ @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @ContextConfiguration(classes = WebConfig.class) -public class EmployeeTestWithoutMockMvc { +public class EmployeeIntegrationTest { @Autowired private EmployeeController employeeController; diff --git a/spring-mvc-java/src/test/java/com/baeldung/web/controller/GreetControllerIntegrationTest.java b/spring-mvc-java/src/test/java/com/baeldung/web/controller/GreetControllerIntegrationTest.java index ca8c37175e0d..b628228b7ee4 100644 --- a/spring-mvc-java/src/test/java/com/baeldung/web/controller/GreetControllerIntegrationTest.java +++ b/spring-mvc-java/src/test/java/com/baeldung/web/controller/GreetControllerIntegrationTest.java @@ -21,10 +21,11 @@ import org.springframework.web.context.WebApplicationContext; import com.baeldung.spring.web.config.ApplicationConfig; +import com.baeldung.spring.web.config.WebConfig; @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration -@ContextConfiguration(classes = { ApplicationConfig.class }) +@ContextConfiguration(classes = { ApplicationConfig.class, WebConfig.class }) public class GreetControllerIntegrationTest { @Autowired diff --git a/spring-mvc-java/src/test/java/com/baeldung/web/controller/RequestMapingShortcutsUnitTest.java b/spring-mvc-java/src/test/java/com/baeldung/web/controller/RequestMapingShortcutsIntegrationTest.java similarity index 98% rename from spring-mvc-java/src/test/java/com/baeldung/web/controller/RequestMapingShortcutsUnitTest.java rename to spring-mvc-java/src/test/java/com/baeldung/web/controller/RequestMapingShortcutsIntegrationTest.java index d02a7140b5e2..fb2190502720 100644 --- a/spring-mvc-java/src/test/java/com/baeldung/web/controller/RequestMapingShortcutsUnitTest.java +++ b/spring-mvc-java/src/test/java/com/baeldung/web/controller/RequestMapingShortcutsIntegrationTest.java @@ -21,7 +21,7 @@ @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @ContextConfiguration(classes = WebConfig.class) -public class RequestMapingShortcutsUnitTest { +public class RequestMapingShortcutsIntegrationTest { @Autowired private WebApplicationContext ctx; diff --git a/spring-mvc-no-xml/README.md b/spring-mvc-no-xml/README.md deleted file mode 100644 index 208cb35f7823..000000000000 --- a/spring-mvc-no-xml/README.md +++ /dev/null @@ -1,9 +0,0 @@ -========= - -## Spring MVC with NO XML Configuration Example Project - -###The Course -The "REST With Spring" Classes: http://bit.ly/restwithspring - -### Relevant Articles: -- diff --git a/spring-mvc-no-xml/pom.xml b/spring-mvc-no-xml/pom.xml deleted file mode 100644 index 4437661199d1..000000000000 --- a/spring-mvc-no-xml/pom.xml +++ /dev/null @@ -1,175 +0,0 @@ - - 4.0.0 - com.baeldung - 0.1-SNAPSHOT - spring-mvc-no-xml - - spring-mvc-no-xml - war - - - - - - - org.springframework - spring-web - ${org.springframework.version} - - - org.springframework - spring-webmvc - ${org.springframework.version} - - - - - - javax.servlet - javax.servlet-api - ${javax.servlet-api.version} - provided - - - - javax.servlet - jstl - ${jstl.version} - runtime - - - - - - org.slf4j - slf4j-api - ${org.slf4j.version} - - - ch.qos.logback - logback-classic - ${logback.version} - - - - org.slf4j - jcl-over-slf4j - ${org.slf4j.version} - - - - org.slf4j - log4j-over-slf4j - ${org.slf4j.version} - - - - - - junit - junit - ${junit.version} - test - - - - org.hamcrest - hamcrest-core - ${org.hamcrest.version} - test - - - org.hamcrest - hamcrest-library - ${org.hamcrest.version} - test - - - - org.mockito - mockito-core - ${mockito.version} - test - - - - - - spring-mvc-no-xml - - - src/main/resources - true - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - ${maven-compiler-plugin.version} - - 1.8 - 1.8 - - - - - org.apache.maven.plugins - maven-war-plugin - ${maven-war-plugin.version} - - false - - - - - org.apache.maven.plugins - maven-surefire-plugin - ${maven-surefire-plugin.version} - - - - - - - - - - - - - - - - - 4.3.4.RELEASE - - - 1.7.21 - 1.1.7 - - - 1.3 - 4.12 - 1.10.19 - - 3.1.0 - 1.2 - - 4.4.5 - 4.5.2 - - 2.9.0 - - - 3.6.0 - 2.6 - 2.19.1 - 2.7 - 1.6.1 - - - - \ No newline at end of file diff --git a/spring-mvc-no-xml/src/main/java/org/baeldung/servlet/WebAppNew.java b/spring-mvc-no-xml/src/main/java/org/baeldung/servlet/WebAppNew.java deleted file mode 100644 index 2422f2110aba..000000000000 --- a/spring-mvc-no-xml/src/main/java/org/baeldung/servlet/WebAppNew.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.baeldung.servlet; - -import javax.servlet.ServletRegistration.Dynamic; - -import org.baeldung.spring.ClientWebConfig; -import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; - -/** - * Further reading:
- * - http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/mvc.html#mvc-container-config
- * - http://geowarin.wordpress.com/2013/01/23/complete-example-of-a-spring-mvc-3-2-project/
- * - http://www.objectpartners.com/2012/01/16/introduction-to-servlet-3-0/
- */ -public class WebAppNew extends AbstractAnnotationConfigDispatcherServletInitializer { - - public WebAppNew() { - super(); - } - - // API - - @Override - protected Class[] getRootConfigClasses() { - return null; - } - - @Override - protected Class[] getServletConfigClasses() { - return new Class[] { ClientWebConfig.class }; - } - - @Override - protected String[] getServletMappings() { - return new String[] { "/" }; - } - - @Override - protected void customizeRegistration(final Dynamic registration) { - super.customizeRegistration(registration); - } - -} diff --git a/spring-mvc-no-xml/src/main/java/org/baeldung/servlet/WebAppOld.java b/spring-mvc-no-xml/src/main/java/org/baeldung/servlet/WebAppOld.java deleted file mode 100644 index 85c67c094de2..000000000000 --- a/spring-mvc-no-xml/src/main/java/org/baeldung/servlet/WebAppOld.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.baeldung.servlet; - -// public class WebApp implements WebApplicationInitializer { -// -// public WebApp() { -// super(); -// } -// -// // API -// -// @Override -// public void onStartup(final ServletContext servletContext) throws ServletException { -// final AnnotationConfigWebApplicationContext root = new AnnotationConfigWebApplicationContext(); -// root.setServletContext(servletContext); -// root.scan("org.baeldung.spring"); -// root.refresh(); -// -// final Dynamic servlet = servletContext.addServlet("mvc", new DispatcherServlet(root)); -// servlet.setLoadOnStartup(1); -// servlet.addMapping("/"); -// } -// -// } diff --git a/spring-mvc-no-xml/src/main/java/org/baeldung/spring/ClientWebConfig.java b/spring-mvc-no-xml/src/main/java/org/baeldung/spring/ClientWebConfig.java deleted file mode 100644 index eaefb0984b77..000000000000 --- a/spring-mvc-no-xml/src/main/java/org/baeldung/spring/ClientWebConfig.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.baeldung.spring; - -import org.springframework.context.annotation.Bean; -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.ViewControllerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; -import org.springframework.web.servlet.view.InternalResourceViewResolver; -import org.springframework.web.servlet.view.JstlView; - -@EnableWebMvc -@Configuration -public class ClientWebConfig extends WebMvcConfigurerAdapter { - - public ClientWebConfig() { - super(); - } - - // API - - @Override - public void addViewControllers(final ViewControllerRegistry registry) { - super.addViewControllers(registry); - - registry.addViewController("/sample.html"); - } - - @Bean - public ViewResolver viewResolver() { - final InternalResourceViewResolver bean = new InternalResourceViewResolver(); - - bean.setViewClass(JstlView.class); - bean.setPrefix("/WEB-INF/view/"); - bean.setSuffix(".jsp"); - - return bean; - } -} \ No newline at end of file diff --git a/spring-mvc-no-xml/src/main/resources/logback.xml b/spring-mvc-no-xml/src/main/resources/logback.xml deleted file mode 100644 index 1146dade632e..000000000000 --- a/spring-mvc-no-xml/src/main/resources/logback.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/spring-mvc-no-xml/src/main/resources/webSecurityConfig.xml b/spring-mvc-no-xml/src/main/resources/webSecurityConfig.xml deleted file mode 100644 index febac349b0e3..000000000000 --- a/spring-mvc-no-xml/src/main/resources/webSecurityConfig.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/spring-mvc-no-xml/src/main/webapp/WEB-INF/mvc-servlet.xml b/spring-mvc-no-xml/src/main/webapp/WEB-INF/mvc-servlet.xml deleted file mode 100644 index 4ba96424480e..000000000000 --- a/spring-mvc-no-xml/src/main/webapp/WEB-INF/mvc-servlet.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/spring-mvc-no-xml/src/main/webapp/WEB-INF/view/sample.jsp b/spring-mvc-no-xml/src/main/webapp/WEB-INF/view/sample.jsp deleted file mode 100644 index 7cc14b5dcddf..000000000000 --- a/spring-mvc-no-xml/src/main/webapp/WEB-INF/view/sample.jsp +++ /dev/null @@ -1,7 +0,0 @@ - - - - -

This is the body of the sample view

- - \ No newline at end of file diff --git a/spring-mvc-no-xml/src/main/webapp/WEB-INF/web_old.xml b/spring-mvc-no-xml/src/main/webapp/WEB-INF/web_old.xml deleted file mode 100644 index e2f52148c303..000000000000 --- a/spring-mvc-no-xml/src/main/webapp/WEB-INF/web_old.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - Spring MVC No XML Application - - - - contextClass - - org.springframework.web.context.support.AnnotationConfigWebApplicationContext - - - - contextConfigLocation - org.baeldung.spring - - - - org.springframework.web.context.ContextLoaderListener - - - - - mvc - org.springframework.web.servlet.DispatcherServlet - 1 - - - mvc - / - - - - - - index.html - - - \ No newline at end of file diff --git a/spring-mvc-simple/pom.xml b/spring-mvc-simple/pom.xml index 4ab5bd9d1ef7..6e96775f262a 100644 --- a/spring-mvc-simple/pom.xml +++ b/spring-mvc-simple/pom.xml @@ -12,6 +12,13 @@ 4.3.4.RELEASE 3.5.1 2.6 + 1.2 + 2.3.1 + 3.1.0 + 1.8 + 5.3.3.Final + enter-location-of-server + 1.3.2 @@ -20,11 +27,6 @@ javax.servlet-api 3.1.0 - - commons-logging - commons-logging - 1.2 - org.springframework spring-webmvc @@ -39,7 +41,40 @@ org.springframework spring-core ${springframework.version} + + + commons-logging + commons-logging + + + + javax.servlet.jsp + javax.servlet.jsp-api + ${javax.servlet.jsp-api.version} + + + + javax.servlet + jstl + ${jstl.version} + + + + org.hibernate + hibernate-validator + ${hibernate-validator.version} + + + org.springframework + spring-webmvc + ${springframework.version} + + + commons-fileupload + commons-fileupload + ${fileupload.version} + @@ -61,6 +96,7 @@ src/main/webapp springMvcSimple false + ${deploy-path} diff --git a/spring-mvc-simple/src/main/java/com/baeldung/spring/configuration/ApplicationConfiguration.java b/spring-mvc-simple/src/main/java/com/baeldung/spring/configuration/ApplicationConfiguration.java new file mode 100644 index 000000000000..9ace968bbe51 --- /dev/null +++ b/spring-mvc-simple/src/main/java/com/baeldung/spring/configuration/ApplicationConfiguration.java @@ -0,0 +1,37 @@ +package com.baeldung.spring.configuration; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.multipart.MultipartResolver; +import org.springframework.web.multipart.commons.CommonsMultipartResolver; +import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; +import org.springframework.web.servlet.view.InternalResourceViewResolver; + +@Configuration +@EnableWebMvc +@ComponentScan(basePackages = "com.baeldung.springmvcforms") +class ApplicationConfiguration extends WebMvcConfigurerAdapter { + + @Override + public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { + configurer.enable(); + } + + @Bean + public InternalResourceViewResolver jspViewResolver() { + InternalResourceViewResolver bean = new InternalResourceViewResolver(); + bean.setPrefix("/WEB-INF/views/"); + bean.setSuffix(".jsp"); + return bean; + } + + @Bean + public MultipartResolver multipartResolver() { + CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(); + multipartResolver.setMaxUploadSize(5242880); + return multipartResolver; + } +} diff --git a/spring-mvc-simple/src/main/java/com/baeldung/spring/configuration/WebInitializer.java b/spring-mvc-simple/src/main/java/com/baeldung/spring/configuration/WebInitializer.java new file mode 100644 index 000000000000..d6bbf5eabd2b --- /dev/null +++ b/spring-mvc-simple/src/main/java/com/baeldung/spring/configuration/WebInitializer.java @@ -0,0 +1,48 @@ +package com.baeldung.spring.configuration; + +import org.springframework.web.WebApplicationInitializer; +import org.springframework.web.context.ContextLoaderListener; +import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; +import org.springframework.web.servlet.DispatcherServlet; + +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRegistration; + +public class WebInitializer implements WebApplicationInitializer { + + public void onStartup(ServletContext container) throws ServletException { + + AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); + ctx.register(ApplicationConfiguration.class); + ctx.setServletContext(container); + + // Manage the lifecycle of the root application context + container.addListener(new ContextLoaderListener(ctx)); + + ServletRegistration.Dynamic servlet = container.addServlet("dispatcher", new DispatcherServlet(ctx)); + + servlet.setLoadOnStartup(1); + servlet.addMapping("/"); + + } +// @Override +// public void onStartup(ServletContext container) { +// // Create the 'root' Spring application context +// AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext(); +// rootContext.register(ServiceConfig.class, JPAConfig.class, SecurityConfig.class); +// +// // Manage the lifecycle of the root application context +// container.addListener(new ContextLoaderListener(rootContext)); +// +// // Create the dispatcher servlet's Spring application context +// AnnotationConfigWebApplicationContext dispatcherServlet = new AnnotationConfigWebApplicationContext(); +// dispatcherServlet.register(MvcConfig.class); +// +// // Register and map the dispatcher servlet +// ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher", new DispatcherServlet(dispatcherServlet)); +// dispatcher.setLoadOnStartup(1); +// dispatcher.addMapping("/"); +// +// } +} diff --git a/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/CustomerController.java b/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/CustomerController.java new file mode 100644 index 000000000000..8ecfce58e362 --- /dev/null +++ b/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/CustomerController.java @@ -0,0 +1,42 @@ +package com.baeldung.spring.controller; + +import javax.validation.Valid; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.servlet.ModelAndView; + +import com.baeldung.spring.domain.Customer; +import com.baeldung.spring.validator.CustomerValidator; + +@Controller +public class CustomerController { + + @Autowired + CustomerValidator validator; + + @RequestMapping(value = "/customer", method = RequestMethod.GET) + public ModelAndView showForm() { + return new ModelAndView("customerHome", "customer", new Customer()); + } + + @PostMapping("/addCustomer") + public String submit(@Valid @ModelAttribute("customer") final Customer customer, final BindingResult result, final ModelMap model) { + validator.validate(customer, result); + if (result.hasErrors()) { + return "customerHome"; + } + model.addAttribute("customerId", customer.getCustomerId()); + model.addAttribute("customerName", customer.getCustomerName()); + model.addAttribute("customerContact", customer.getCustomerContact()); + model.addAttribute("customerEmail", customer.getCustomerEmail()); + return "customerView"; + } + +} diff --git a/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/EmployeeController.java b/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/EmployeeController.java new file mode 100644 index 000000000000..6543a98af1ed --- /dev/null +++ b/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/EmployeeController.java @@ -0,0 +1,47 @@ +package com.baeldung.spring.controller; + +import java.util.HashMap; +import java.util.Map; + +import javax.validation.Valid; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; +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.servlet.ModelAndView; + +import com.baeldung.spring.domain.Employee; + +@Controller +public class EmployeeController { + + Map employeeMap = new HashMap<>(); + + @RequestMapping(value = "/employee", method = RequestMethod.GET) + public ModelAndView showForm() { + return new ModelAndView("employeeHome", "employee", new Employee()); + } + + @RequestMapping(value = "/employee/{Id}", produces = { "application/json", "application/xml" }, method = RequestMethod.GET) + public @ResponseBody Employee getEmployeeById(@PathVariable final long Id) { + return employeeMap.get(Id); + } + + @RequestMapping(value = "/addEmployee", method = RequestMethod.POST) + public String submit(@Valid @ModelAttribute("employee") final Employee employee, final BindingResult result, final ModelMap model) { + if (result.hasErrors()) { + return "error"; + } + model.addAttribute("name", employee.getName()); + model.addAttribute("contactNumber", employee.getContactNumber()); + model.addAttribute("id", employee.getId()); + employeeMap.put(employee.getId(), employee); + return "employeeView"; + } + +} diff --git a/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/FileUploadController.java b/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/FileUploadController.java new file mode 100644 index 000000000000..47af2ab50dc8 --- /dev/null +++ b/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/FileUploadController.java @@ -0,0 +1,52 @@ +package com.baeldung.spring.controller; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.multipart.MaxUploadSizeExceededException; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.HandlerExceptionResolver; +import org.springframework.web.servlet.ModelAndView; + +@Controller +public class FileUploadController implements HandlerExceptionResolver { + + @RequestMapping(value = "/uploadFile", method = RequestMethod.GET) + public String getImageView() { + return "file"; + } + + @RequestMapping(value = "/uploadFile", method = RequestMethod.POST) + public ModelAndView uploadFile(MultipartFile file) throws IOException{ + ModelAndView modelAndView = new ModelAndView("file"); + + InputStream in = file.getInputStream(); + File currDir = new File("."); + String path = currDir.getAbsolutePath(); + FileOutputStream f = new FileOutputStream(path.substring(0, path.length()-1)+ file.getOriginalFilename()); + int ch = 0; + while ((ch = in.read()) != -1) { + f.write(ch); + } + f.flush(); + f.close(); + + modelAndView.getModel().put("message", "File uploaded successfully!"); + return modelAndView; + } + + @Override + public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object object, Exception exc) { + ModelAndView modelAndView = new ModelAndView("file"); + if (exc instanceof MaxUploadSizeExceededException) { + modelAndView.getModel().put("message", "File size exceeds limit!"); + } + return modelAndView; + } +} diff --git a/spring-mvc-simple/src/main/java/com/baeldung/spring/domain/Customer.java b/spring-mvc-simple/src/main/java/com/baeldung/spring/domain/Customer.java new file mode 100644 index 000000000000..09322105f80d --- /dev/null +++ b/spring-mvc-simple/src/main/java/com/baeldung/spring/domain/Customer.java @@ -0,0 +1,45 @@ +package com.baeldung.spring.domain; + +public class Customer { + private String customerId; + private String customerName; + private String customerContact; + private String customerEmail; + + public Customer() { + super(); + } + + public String getCustomerId() { + return customerId; + } + + public void setCustomerId(String customerId) { + this.customerId = customerId; + } + + public String getCustomerName() { + return customerName; + } + + public void setCustomerName(String customerName) { + this.customerName = customerName; + } + + public String getCustomerContact() { + return customerContact; + } + + public void setCustomerContact(String customerContact) { + this.customerContact = customerContact; + } + + public String getCustomerEmail() { + return customerEmail; + } + + public void setCustomerEmail(String customerEmail) { + this.customerEmail = customerEmail; + } + +} diff --git a/spring-mvc-simple/src/main/java/com/baeldung/spring/domain/Employee.java b/spring-mvc-simple/src/main/java/com/baeldung/spring/domain/Employee.java new file mode 100644 index 000000000000..900770b87336 --- /dev/null +++ b/spring-mvc-simple/src/main/java/com/baeldung/spring/domain/Employee.java @@ -0,0 +1,46 @@ +package com.baeldung.spring.domain; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +public class Employee { + + private long id; + + @NotNull + @Size(min = 5) + private String name; + + @NotNull + @Size(min = 7) + private String contactNumber; + + public Employee() { + super(); + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public long getId() { + return id; + } + + public void setId(final long id) { + this.id = id; + } + + public String getContactNumber() { + return contactNumber; + } + + public void setContactNumber(final String contactNumber) { + this.contactNumber = contactNumber; + } + +} diff --git a/spring-mvc-simple/src/main/java/com/baeldung/spring/interceptor/FileUploadExceptionAdvice.java b/spring-mvc-simple/src/main/java/com/baeldung/spring/interceptor/FileUploadExceptionAdvice.java new file mode 100644 index 000000000000..2f3c44cf12d9 --- /dev/null +++ b/spring-mvc-simple/src/main/java/com/baeldung/spring/interceptor/FileUploadExceptionAdvice.java @@ -0,0 +1,19 @@ +package com.baeldung.spring.interceptor; + +import org.springframework.web.servlet.ModelAndView; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.web.multipart.MaxUploadSizeExceededException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +@ControllerAdvice +public class FileUploadExceptionAdvice { + + @ExceptionHandler(MaxUploadSizeExceededException.class) + public ModelAndView handleMaxSizeException(MaxUploadSizeExceededException exc, HttpServletRequest request, HttpServletResponse response){ + ModelAndView modelAndView = new ModelAndView("file"); + modelAndView.getModel().put("message", "File too large!"); + return modelAndView; + } +} \ No newline at end of file diff --git a/spring-mvc-simple/src/main/java/com/baeldung/spring/validator/CustomerValidator.java b/spring-mvc-simple/src/main/java/com/baeldung/spring/validator/CustomerValidator.java new file mode 100644 index 000000000000..2515e8d31fbc --- /dev/null +++ b/spring-mvc-simple/src/main/java/com/baeldung/spring/validator/CustomerValidator.java @@ -0,0 +1,28 @@ +package com.baeldung.spring.validator; + +import org.springframework.stereotype.Component; +import org.springframework.validation.Errors; +import org.springframework.validation.ValidationUtils; +import org.springframework.validation.Validator; + +import com.baeldung.spring.domain.Customer; + +@Component +public class CustomerValidator implements Validator { + + @Override + public boolean supports(Class clazz) { + return Customer.class.isAssignableFrom(clazz); + } + + @Override + public void validate(Object target, Errors errors) { + + ValidationUtils.rejectIfEmptyOrWhitespace(errors, "customerId", "error.customerId", "Customer Id is required."); + ValidationUtils.rejectIfEmptyOrWhitespace(errors, "customerName", "error.customerName", "Customer Name is required."); + ValidationUtils.rejectIfEmptyOrWhitespace(errors, "customerContact", "error.customerNumber", "Customer Contact is required."); + ValidationUtils.rejectIfEmptyOrWhitespace(errors, "customerEmail", "error.customerEmail", "Customer Email is required."); + + } + +} diff --git a/spring-mvc-simple/src/main/resources/spring-servlet_AnnotationMethodHandlerAdapter.xml b/spring-mvc-simple/src/main/resources/spring-servlet_AnnotationMethodHandlerAdapter.xml index a8071a622f6b..430b84901299 100644 --- a/spring-mvc-simple/src/main/resources/spring-servlet_AnnotationMethodHandlerAdapter.xml +++ b/spring-mvc-simple/src/main/resources/spring-servlet_AnnotationMethodHandlerAdapter.xml @@ -1,26 +1,26 @@ - - - - - - - - - + + + + + + + + + \ No newline at end of file diff --git a/spring-mvc-simple/src/main/resources/spring-servlet_RequestMappingHandlerAdapter.xml b/spring-mvc-simple/src/main/resources/spring-servlet_RequestMappingHandlerAdapter.xml index b32a213eb3a2..d3783c2e676b 100644 --- a/spring-mvc-simple/src/main/resources/spring-servlet_RequestMappingHandlerAdapter.xml +++ b/spring-mvc-simple/src/main/resources/spring-servlet_RequestMappingHandlerAdapter.xml @@ -1,28 +1,28 @@ - - - - - - - - - + + + + + + + + + \ No newline at end of file diff --git a/spring-mvc-simple/src/main/resources/spring-servlet_SimpleControllerHandlerAdapter.xml b/spring-mvc-simple/src/main/resources/spring-servlet_SimpleControllerHandlerAdapter.xml index 60c9b9e3df8a..1d6e5628df56 100644 --- a/spring-mvc-simple/src/main/resources/spring-servlet_SimpleControllerHandlerAdapter.xml +++ b/spring-mvc-simple/src/main/resources/spring-servlet_SimpleControllerHandlerAdapter.xml @@ -1,25 +1,25 @@ - - - - - - - - + + + + + + + + \ No newline at end of file diff --git a/spring-mvc-simple/src/main/webapp/WEB-INF/Greeting.jsp b/spring-mvc-simple/src/main/webapp/WEB-INF/Greeting.jsp index 820d2f380fb7..7f7fbccccd2e 100644 --- a/spring-mvc-simple/src/main/webapp/WEB-INF/Greeting.jsp +++ b/spring-mvc-simple/src/main/webapp/WEB-INF/Greeting.jsp @@ -1,5 +1,6 @@ - - -

Hello ${message}

- + + +

Hello ${message}

+ + \ No newline at end of file diff --git a/spring-mvc-simple/src/main/webapp/WEB-INF/views/Greeting.jsp b/spring-mvc-simple/src/main/webapp/WEB-INF/views/Greeting.jsp new file mode 100644 index 000000000000..efd48179f9ba --- /dev/null +++ b/spring-mvc-simple/src/main/webapp/WEB-INF/views/Greeting.jsp @@ -0,0 +1,5 @@ + + +

Hello ${message}

+ + \ No newline at end of file diff --git a/spring-mvc-simple/src/main/webapp/WEB-INF/views/customerHome.jsp b/spring-mvc-simple/src/main/webapp/WEB-INF/views/customerHome.jsp new file mode 100644 index 000000000000..df34a47cc062 --- /dev/null +++ b/spring-mvc-simple/src/main/webapp/WEB-INF/views/customerHome.jsp @@ -0,0 +1,47 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> + + + +Form Example - Add Customer + + + +

Welcome, Enter The Customer Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Customer Id
Customer Name
Customer Contact
Customer Email
+
+ + + + \ No newline at end of file diff --git a/spring-mvc-simple/src/main/webapp/WEB-INF/views/customerView.jsp b/spring-mvc-simple/src/main/webapp/WEB-INF/views/customerView.jsp new file mode 100644 index 000000000000..ab2631bd02cf --- /dev/null +++ b/spring-mvc-simple/src/main/webapp/WEB-INF/views/customerView.jsp @@ -0,0 +1,28 @@ +<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%> + + +Spring MVC Form Handling + + + +

Submitted Customer Information

+ + + + + + + + + + + + + + + + + +
Customer Id :${customerId}
Customer Name :${customerName}
Customer Contact :${customerContact}
Customer Email :${customerEmail}
+ + \ No newline at end of file diff --git a/spring-mvc-simple/src/main/webapp/WEB-INF/views/employeeHome.jsp b/spring-mvc-simple/src/main/webapp/WEB-INF/views/employeeHome.jsp new file mode 100644 index 000000000000..5ed572000a3b --- /dev/null +++ b/spring-mvc-simple/src/main/webapp/WEB-INF/views/employeeHome.jsp @@ -0,0 +1,33 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> + + + +Form Example - Register an Employee + + +

Welcome, Enter The Employee Details

+ + + + + + + + + + + + + + + + + + +
Name
Id
Contact Number
+
+ + + + \ No newline at end of file diff --git a/spring-mvc-simple/src/main/webapp/WEB-INF/views/employeeView.jsp b/spring-mvc-simple/src/main/webapp/WEB-INF/views/employeeView.jsp new file mode 100644 index 000000000000..1457bc5fc8d3 --- /dev/null +++ b/spring-mvc-simple/src/main/webapp/WEB-INF/views/employeeView.jsp @@ -0,0 +1,24 @@ +<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%> + + +Spring MVC Form Handling + + + +

Submitted Employee Information

+ + + + + + + + + + + + + +
Name :${name}
ID :${id}
Contact Number :${contactNumber}
+ + \ No newline at end of file diff --git a/spring-mvc-simple/src/main/webapp/WEB-INF/views/error.jsp b/spring-mvc-simple/src/main/webapp/WEB-INF/views/error.jsp new file mode 100644 index 000000000000..8f3d83af1767 --- /dev/null +++ b/spring-mvc-simple/src/main/webapp/WEB-INF/views/error.jsp @@ -0,0 +1,20 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> + + + + +SpringMVCExample + + + +

Pleas enter the correct details

+ + + + +
Retry
+ + + + \ No newline at end of file diff --git a/spring-mvc-simple/src/main/webapp/WEB-INF/views/file.jsp b/spring-mvc-simple/src/main/webapp/WEB-INF/views/file.jsp new file mode 100644 index 000000000000..0ed8dae5edde --- /dev/null +++ b/spring-mvc-simple/src/main/webapp/WEB-INF/views/file.jsp @@ -0,0 +1,23 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> + + + + +Upload file + + + +
+ + +
+
+${message } +

+
+ +
+ + \ No newline at end of file diff --git a/spring-mvc-tiles/pom.xml b/spring-mvc-tiles/pom.xml index 400f79fbe161..684c3abec7be 100644 --- a/spring-mvc-tiles/pom.xml +++ b/spring-mvc-tiles/pom.xml @@ -25,6 +25,12 @@ org.springframework spring-core ${springframework.version} + + + commons-logging + commons-logging + + org.springframework diff --git a/spring-mvc-velocity/pom.xml b/spring-mvc-velocity/pom.xml index 31f2d19375ac..51348acd4ac4 100644 --- a/spring-mvc-velocity/pom.xml +++ b/spring-mvc-velocity/pom.xml @@ -26,6 +26,12 @@ org.springframework spring-core ${org.springframework.version} + + + commons-logging + commons-logging + + org.springframework @@ -52,6 +58,12 @@ org.apache.velocity velocity-tools ${velocity-tools.version} + + + commons-logging + commons-logging + + @@ -136,8 +148,9 @@ org.apache.maven.plugins maven-surefire-plugin - ${maven-surefire-plugin.version} + 3 + true **/*IntegrationTest.java diff --git a/spring-mvc-web-vs-initializer/pom.xml b/spring-mvc-web-vs-initializer/pom.xml index 8fe88939038b..1d02039ead22 100644 --- a/spring-mvc-web-vs-initializer/pom.xml +++ b/spring-mvc-web-vs-initializer/pom.xml @@ -24,6 +24,12 @@ org.springframework spring-web + + + commons-logging + commons-logging + + org.springframework @@ -72,10 +78,6 @@ jcl-over-slf4j - - org.slf4j - log4j-over-slf4j - diff --git a/spring-mvc-xml/pom.xml b/spring-mvc-xml/pom.xml index 86eb17da647c..142b400e5f15 100644 --- a/spring-mvc-xml/pom.xml +++ b/spring-mvc-xml/pom.xml @@ -16,6 +16,12 @@ org.springframework spring-web ${org.springframework.version} + + + commons-logging + commons-logging + + org.springframework @@ -64,11 +70,6 @@ ${org.slf4j.version} - - org.slf4j - log4j-over-slf4j - ${org.slf4j.version} - @@ -121,7 +122,11 @@ com.fasterxml.jackson.core jackson-databind - + + commons-logging + commons-logging + + diff --git a/spring-mvc-xml/src/main/resources/logback.xml b/spring-mvc-xml/src/main/resources/logback.xml index 1146dade632e..ec0dc2469ae0 100644 --- a/spring-mvc-xml/src/main/resources/logback.xml +++ b/spring-mvc-xml/src/main/resources/logback.xml @@ -1,20 +1,19 @@ + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - + + - - - - - - - - - + + + + + \ No newline at end of file diff --git a/spring-mybatis/pom.xml b/spring-mybatis/pom.xml new file mode 100644 index 000000000000..b0eaab5b3aa4 --- /dev/null +++ b/spring-mybatis/pom.xml @@ -0,0 +1,62 @@ + + 4.0.0 + com.baeldung + spring-mybatis + jar + 0.0.1-SNAPSHOT + spring-mybatis Maven Webapp + http://maven.apache.org + + + org.mybatis + mybatis + 3.1.1 + + + org.mybatis + mybatis-spring + 1.1.1 + + + org.springframework + spring-context-support + 3.1.1.RELEASE + + + org.springframework + spring-test + 3.1.1.RELEASE + test + + + mysql + mysql-connector-java + 5.1.40 + + + javax.servlet + jstl + 1.2 + + + org.springframework + spring-webmvc + 3.2.4.RELEASE + + + javax.servlet + servlet-api + 2.5 + + + junit + junit + 3.8.1 + test + + + + spring-mybatis + + diff --git a/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/application/SpringMyBatisApplication.java b/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/application/SpringMyBatisApplication.java new file mode 100644 index 000000000000..acfaff266946 --- /dev/null +++ b/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/application/SpringMyBatisApplication.java @@ -0,0 +1,48 @@ +package com.baeldung.spring.mybatis.application; + +import java.util.Date; + +import org.springframework.context.support.ClassPathXmlApplicationContext; + +import com.baeldung.spring.mybatis.model.Student; +import com.baeldung.spring.mybatis.service.StudentService; + +public class SpringMyBatisApplication { + + public static void main(String[] args){ + ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("mybatis-spring.xml"); + + StudentService studentService = (StudentService) ctx.getBean("studentService"); + Student student = new Student(); + student.setFirstName("Santosh"); + student.setLastName("B S"); + student.setEmailAddress("santosh.bse@gmail.com"); + student.setPassword("Test123"); + student.setDateOfBirth(new Date()); + student.setUserName("santoshbs1"); + + boolean result = studentService.insertStudent(student); + if(result){ + System.out.println("Student record saved successfully"); + } + else{ + System.out.println("Encountered an error while saving student data"); + } + + final String userName = "santosh"; + Student matchingStudent = studentService.getStudentByUserName(userName); + if(matchingStudent == null){ + System.out.println("No matching student found for User Name - " + userName); + } + else{ + System.out.println("Student Details are as follows : "); + System.out.println("First Name : " + matchingStudent.getFirstName()); + System.out.println("Last Name : " + matchingStudent.getLastName()); + System.out.println("EMail : " + matchingStudent.getEmailAddress()); + System.out.println("DOB : " + matchingStudent.getDateOfBirth()); + System.out.println("User Name : " + matchingStudent.getUserName()); + } + + } + +} diff --git a/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/controller/StudentController.java b/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/controller/StudentController.java new file mode 100644 index 000000000000..427613f23fa7 --- /dev/null +++ b/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/controller/StudentController.java @@ -0,0 +1,60 @@ +package com.baeldung.spring.mybatis.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.ui.ModelMap; +import org.springframework.validation.BindingResult; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.SessionAttributes; + +import com.baeldung.spring.mybatis.model.Student; +import com.baeldung.spring.mybatis.model.StudentLogin; +import com.baeldung.spring.mybatis.service.StudentService; + +@Controller +@SessionAttributes("student") +public class StudentController { + + @Autowired + private StudentService studentService; + + @RequestMapping(value = "/signup", method = RequestMethod.GET) + public String signup(Model model) { + Student student = new Student(); + model.addAttribute("student", student); + return "signup"; + } + + @RequestMapping(value = "/signup", method = RequestMethod.POST) + public String signup(@Validated @ModelAttribute("student") Student student, BindingResult result, ModelMap model) { + if (studentService.getStudentByUserName(student.getUserName())) { + model.addAttribute("message", "User Name exists. Try another user name"); + return "signup"; + } else { + studentService.insertStudent(student); + model.addAttribute("message", "Saved student details"); + return "redirect:login.html"; + } + } + + @RequestMapping(value = "/login", method = RequestMethod.GET) + public String login(Model model) { + StudentLogin studentLogin = new StudentLogin(); + model.addAttribute("studentLogin", studentLogin); + return "login"; + } + + @RequestMapping(value = "/login", method = RequestMethod.POST) + public String login(@ModelAttribute("studentLogin") StudentLogin studentLogin, BindingResult result, ModelMap model) { + boolean found = studentService.getStudentByLogin(studentLogin.getUserName(), studentLogin.getPassword()); + if (found) { + return "success"; + } else { + return "failure"; + } + } +} \ No newline at end of file diff --git a/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/mappers/StudentMapper.java b/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/mappers/StudentMapper.java new file mode 100644 index 000000000000..cf3584f7b180 --- /dev/null +++ b/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/mappers/StudentMapper.java @@ -0,0 +1,19 @@ +package com.baeldung.spring.mybatis.mappers; + +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Options; +import org.apache.ibatis.annotations.Select; + +import com.baeldung.spring.mybatis.model.Student; + +public interface StudentMapper { + @Insert("INSERT INTO student(userName, password, firstName,lastName, dateOfBirth, emailAddress) VALUES" + + "(#{userName},#{password}, #{firstName}, #{lastName}, #{dateOfBirth}, #{emailAddress})") + @Options(useGeneratedKeys = true, keyProperty = "id", flushCache = true, keyColumn = "id") + public void insertStudent(Student student); + + @Select("SELECT USERNAME as userName, PASSWORD as password, FIRSTNAME as firstName, LASTNAME as lastName, " + + "DATEOFBIRTH as dateOfBirth, EMAILADDRESS as emailAddress " + "FROM student WHERE userName = #{userName}") + public Student getStudentByUserName(String userName); + +} \ No newline at end of file diff --git a/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/model/Student.java b/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/model/Student.java new file mode 100644 index 000000000000..f33dd44f72ca --- /dev/null +++ b/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/model/Student.java @@ -0,0 +1,55 @@ +package com.baeldung.spring.mybatis.model; + +import java.util.Date; + +public class Student { + private Long id; + private String userName; + private String firstName; + private String lastName; + private String password; + private String emailAddress; + private Date dateOfBirth; + public Long getId() { + return id; + } + public void setId(Long id) { + this.id = id; + } + public String getUserName() { + return userName; + } + public void setUserName(String userName) { + this.userName = userName; + } + public String getFirstName() { + return firstName; + } + public void setFirstName(String firstName) { + this.firstName = firstName; + } + public String getLastName() { + return lastName; + } + public void setLastName(String lastName) { + this.lastName = lastName; + } + public String getPassword() { + return password; + } + public void setPassword(String password) { + this.password = password; + } + public String getEmailAddress() { + return emailAddress; + } + public void setEmailAddress(String emailAddress) { + this.emailAddress = emailAddress; + } + public Date getDateOfBirth() { + return dateOfBirth; + } + public void setDateOfBirth(Date dateOfBirth) { + this.dateOfBirth = dateOfBirth; + } +} diff --git a/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/model/StudentLogin.java b/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/model/StudentLogin.java new file mode 100644 index 000000000000..867857b51044 --- /dev/null +++ b/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/model/StudentLogin.java @@ -0,0 +1,18 @@ +package com.baeldung.spring.mybatis.model; + +public class StudentLogin { + private String userName; + private String password; + public String getUserName() { + return userName; + } + public void setUserName(String userName) { + this.userName = userName; + } + public String getPassword() { + return password; + } + public void setPassword(String password) { + this.password = password; + } +} diff --git a/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/service/StudentService.java b/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/service/StudentService.java new file mode 100644 index 000000000000..d26115beee2a --- /dev/null +++ b/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/service/StudentService.java @@ -0,0 +1,9 @@ +package com.baeldung.spring.mybatis.service; + +import com.baeldung.spring.mybatis.model.Student; + +public interface StudentService { + public boolean insertStudent(Student student); + public Student getStudentByLogin(String userName, String password); + public Student getStudentByUserName(String userName); +} \ No newline at end of file diff --git a/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/service/StudentServiceImpl.java b/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/service/StudentServiceImpl.java new file mode 100644 index 000000000000..538e65048286 --- /dev/null +++ b/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/service/StudentServiceImpl.java @@ -0,0 +1,40 @@ +package com.baeldung.spring.mybatis.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.baeldung.spring.mybatis.mappers.StudentMapper; +import com.baeldung.spring.mybatis.model.Student; + +@Service("studentService") +public class StudentServiceImpl implements StudentService { + + @Autowired + private StudentMapper studentMapper; + + @Transactional + public boolean insertStudent(Student student) { + boolean result=false; + try{ + studentMapper.insertStudent(student); + result = true; + } + catch(Exception ex){ + ex.printStackTrace(); + result = false; + } + return result; + } + + public Student getStudentByLogin(String userName, String password) { + Student student = studentMapper.getStudentByUserName(userName); + return student; + } + + public Student getStudentByUserName(String userName) { + Student student = studentMapper.getStudentByUserName(userName); + return student; + } + +} diff --git a/spring-mybatis/src/main/resources/mybatis-spring.xml b/spring-mybatis/src/main/resources/mybatis-spring.xml new file mode 100644 index 000000000000..9f5bab32471e --- /dev/null +++ b/spring-mybatis/src/main/resources/mybatis-spring.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-mybatis/src/main/webapp/WEB-INF/conf/mybatis-spring.xml b/spring-mybatis/src/main/webapp/WEB-INF/conf/mybatis-spring.xml new file mode 100644 index 000000000000..c8b686358cd2 --- /dev/null +++ b/spring-mybatis/src/main/webapp/WEB-INF/conf/mybatis-spring.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-mybatis/src/main/webapp/WEB-INF/jsp/failure.jsp b/spring-mybatis/src/main/webapp/WEB-INF/jsp/failure.jsp new file mode 100644 index 000000000000..66f16d4e0959 --- /dev/null +++ b/spring-mybatis/src/main/webapp/WEB-INF/jsp/failure.jsp @@ -0,0 +1,36 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> + + + + + +Login Failure + + + + +
+ Login     Signup +
+
+
+

Student Enrollment Login failure

+
+
+
+ + Oh snap! Something is wrong. Change a few things up + and try submitting again. +
+
+
+
+
+ + ">Try + again? + + \ No newline at end of file diff --git a/spring-mybatis/src/main/webapp/WEB-INF/jsp/login.jsp b/spring-mybatis/src/main/webapp/WEB-INF/jsp/login.jsp new file mode 100644 index 000000000000..5a895bb348ca --- /dev/null +++ b/spring-mybatis/src/main/webapp/WEB-INF/jsp/login.jsp @@ -0,0 +1,81 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> + + + + + + + +Student Login + + +
+ Login     Signup +
+ +
+
+
+

Welcome to Online Student Enrollment Application

+

Login to explore the complete features!

+
+
+ +
+
+ +
+
+ Student Enrollment Login Form + + + + + + + + + + + + + + + +
+
+ +      + +
+ +
+
+
+ + + + \ No newline at end of file diff --git a/spring-mybatis/src/main/webapp/WEB-INF/jsp/signup.jsp b/spring-mybatis/src/main/webapp/WEB-INF/jsp/signup.jsp new file mode 100644 index 000000000000..bc628862f34a --- /dev/null +++ b/spring-mybatis/src/main/webapp/WEB-INF/jsp/signup.jsp @@ -0,0 +1,130 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> + +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> + + + + + +Student Signup + + + + + +
+ Login     Signup +
+ +
+
+
+

Welcome to Online Student Enrollment Application

+

Login to explore the complete features!

+
+
+ +
+
+ +
+ +
${message}
+
+
+ +
+ +
+ Student Enrollment Signup Form + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +     + +
+
+
+
+ + \ No newline at end of file diff --git a/spring-mybatis/src/main/webapp/WEB-INF/jsp/success.jsp b/spring-mybatis/src/main/webapp/WEB-INF/jsp/success.jsp new file mode 100644 index 000000000000..7ae37bc24103 --- /dev/null +++ b/spring-mybatis/src/main/webapp/WEB-INF/jsp/success.jsp @@ -0,0 +1,35 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> + + + + +Login Success + + + + +
+ Login     Signup +
+ +
+
+

Student Enrollment Login success

+
+
+
+ + Well done! You successfully logged-into the system. + Now you can explore the complete features! +
+
+
+
+
+ ">Login + as different user? + + \ No newline at end of file diff --git a/spring-mybatis/src/main/webapp/WEB-INF/lib/mysql-connector-java-5.1.40-bin.jar b/spring-mybatis/src/main/webapp/WEB-INF/lib/mysql-connector-java-5.1.40-bin.jar new file mode 100644 index 000000000000..60bef5cfbfbe Binary files /dev/null and b/spring-mybatis/src/main/webapp/WEB-INF/lib/mysql-connector-java-5.1.40-bin.jar differ diff --git a/spring-mybatis/src/main/webapp/WEB-INF/web.xml b/spring-mybatis/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000000..569b5ef9b1a5 --- /dev/null +++ b/spring-mybatis/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,21 @@ + + + + + myBatisSpringServlet + org.springframework.web.servlet.DispatcherServlet + + contextConfigLocation + /WEB-INF/conf/mybatis-spring.xml + + + + + myBatisSpringServlet + *.html + + + MyBatis-Spring Integration + \ No newline at end of file diff --git a/spring-mybatis/src/main/webapp/index.jsp b/spring-mybatis/src/main/webapp/index.jsp new file mode 100644 index 000000000000..b60222e7de59 --- /dev/null +++ b/spring-mybatis/src/main/webapp/index.jsp @@ -0,0 +1,34 @@ + + + + + + + +
+ Login     Signup +
+ +
+
+
+

Welcome to Online Student Enrollment Application

+

Login to explore the complete features!

+
+
+ +
+
+ + + \ No newline at end of file diff --git a/spring-quartz/src/main/resources/logback.xml b/spring-quartz/src/main/resources/logback.xml index be0937fefe0e..ec0dc2469ae0 100644 --- a/spring-quartz/src/main/resources/logback.xml +++ b/spring-quartz/src/main/resources/logback.xml @@ -1,16 +1,19 @@ + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - - %date [%thread] %-5level %logger{36} - %message%n - - - + + - - - - - + + + + + \ No newline at end of file diff --git a/spring-rest-angular/pom.xml b/spring-rest-angular/pom.xml index 62ab03522d6c..b6bc95df81de 100644 --- a/spring-rest-angular/pom.xml +++ b/spring-rest-angular/pom.xml @@ -74,15 +74,6 @@ spring-boot-starter-test test - - org.springframework.boot - spring-boot-starter-data-rest - - - javax.servlet - jstl - 1.2 - diff --git a/spring-rest-angular/src/main/java/org/baeldung/web/controller/EmployeeController.java b/spring-rest-angular/src/main/java/org/baeldung/web/controller/EmployeeController.java deleted file mode 100644 index a8bfc254c3b3..000000000000 --- a/spring-rest-angular/src/main/java/org/baeldung/web/controller/EmployeeController.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.baeldung.web.controller; - -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; - -@Controller -public class EmployeeController { - - @RequestMapping(value = "/employeePage") - public String getEmployeePage() { - return "employee"; - } - -} diff --git a/spring-rest-angular/src/main/java/org/baeldung/web/dao/EmployeeCRUDRepository.java b/spring-rest-angular/src/main/java/org/baeldung/web/dao/EmployeeCRUDRepository.java deleted file mode 100644 index 1e5f81ed453a..000000000000 --- a/spring-rest-angular/src/main/java/org/baeldung/web/dao/EmployeeCRUDRepository.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.baeldung.web.dao; - -import java.util.List; - -import org.baeldung.web.entity.Employee; -import org.springframework.data.repository.CrudRepository; -import org.springframework.data.repository.query.Param; -import org.springframework.data.rest.core.annotation.RepositoryRestResource; - -@RepositoryRestResource(collectionResourceRel = "employee", path = "employees") -public interface EmployeeCRUDRepository extends CrudRepository { - List findByName(@Param("name") String name); -} diff --git a/spring-rest-angular/src/main/java/org/baeldung/web/entity/Employee.java b/spring-rest-angular/src/main/java/org/baeldung/web/entity/Employee.java deleted file mode 100644 index 8d6831726cd6..000000000000 --- a/spring-rest-angular/src/main/java/org/baeldung/web/entity/Employee.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.baeldung.web.entity; - -import java.io.Serializable; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Id; - -@Entity -public class Employee implements Serializable { - - private static final long serialVersionUID = 1L; - - @Id - private long id; - - @Column(nullable = false) - private String name; - - @Column(nullable = false) - private Integer age; - - public Employee() { - } - - public Employee(long id, String name, Integer age) { - super(); - this.id = id; - this.name = name; - this.age = age; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Integer getAge() { - return age; - } - - public void setAge(Integer age) { - this.age = age; - } - - public long getId() { - return id; - } - - public void setId(long id) { - this.id = id; - } - -} diff --git a/spring-rest-angular/src/main/java/org/baeldung/web/main/PersistenceConfig.java b/spring-rest-angular/src/main/java/org/baeldung/web/main/PersistenceConfig.java index 8454ce155a32..e0f6002733e2 100644 --- a/spring-rest-angular/src/main/java/org/baeldung/web/main/PersistenceConfig.java +++ b/spring-rest-angular/src/main/java/org/baeldung/web/main/PersistenceConfig.java @@ -26,7 +26,7 @@ public JdbcTemplate getJdbcTemplate() { @Bean public DataSource dataSource() { EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); - EmbeddedDatabase db = builder.setType(EmbeddedDatabaseType.HSQL).addScript("db/sql/data.sql").addScript("db/sql/employees.sql").build(); + EmbeddedDatabase db = builder.setType(EmbeddedDatabaseType.HSQL).addScript("db/sql/data.sql").build(); return db; } diff --git a/spring-rest-angular/src/main/resources/db/sql/employees.sql b/spring-rest-angular/src/main/resources/db/sql/employees.sql deleted file mode 100644 index 366c0c309af7..000000000000 --- a/spring-rest-angular/src/main/resources/db/sql/employees.sql +++ /dev/null @@ -1,16 +0,0 @@ -CREATE TABLE employee ( - id INTEGER PRIMARY KEY, - name VARCHAR(30), - age INTEGER -); - -INSERT INTO employee (id,name,age) -VALUES (1,'Bryan',20); -INSERT INTO employee (id,name,age) -VALUES (2,'Lisa',30); -INSERT INTO employee (id,name,age) -VALUES (3,'Laura',40); -INSERT INTO employee (id,name,age) -VALUES (4,'Alex',35); -INSERT INTO employee (id,name,age) -VALUES (5,'John',47); diff --git a/spring-rest-angular/src/main/webapp/WEB-INF/pages/employee.html b/spring-rest-angular/src/main/webapp/WEB-INF/pages/employee.html deleted file mode 100644 index 510e981f25e0..000000000000 --- a/spring-rest-angular/src/main/webapp/WEB-INF/pages/employee.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - -Employee CRUD - - - - - - -
- - - - - - - - - - - - - -
ID:
Name:
Age:
-

- Get employee - Update employee - Add employee - Delete employee - -

-

{{message}}

-

{{errorMessage}}

- -
-
- Get all Employees

- Name: - Get employees by name -

-
- {{emp.name}} {{emp.age}} -
-
- - \ No newline at end of file diff --git a/spring-rest-angular/src/main/webapp/view/app.js b/spring-rest-angular/src/main/webapp/view/app.js index 9f78d5adf7d0..b56497807c8d 100644 --- a/spring-rest-angular/src/main/webapp/view/app.js +++ b/spring-rest-angular/src/main/webapp/view/app.js @@ -5,8 +5,8 @@ app.controller('StudentCtrl', ['$scope','StudentService', function ($scope,Stude pageNumber: 1, pageSize: 5, sort: null - }; - + }; + StudentService.getStudents(paginationOptions.pageNumber, paginationOptions.pageSize).success(function(data){ $scope.gridOptions.data = data.content; @@ -54,145 +54,3 @@ app.service('StudentService',['$http', function ($http) { }; }]); - -app.controller('EmployeeCRUDCtrl', ['$scope','EmployeeCRUDService', function ($scope,EmployeeCRUDService) { - - $scope.updateEmployee = function () { - EmployeeCRUDService.updateEmployee($scope.employee.id,$scope.employee.name,$scope.employee.age) - .then(function success(response){ - $scope.message = 'Employee data updated!'; - $scope.errorMessage = ''; - }, - function error(response){ - $scope.errorMessage = 'Error updating Employee!'; - $scope.message = ''; - }); - } - - $scope.getEmployee = function () { - var id = $scope.employee.id; - EmployeeCRUDService.getEmployee($scope.employee.id) - .then(function success(response){ - $scope.employee = response.data; - $scope.employee.id = id; - $scope.message=''; - $scope.errorMessage = ''; - }, - function error (response ){ - $scope.message = ''; - if (response.status === 404){ - $scope.errorMessage = 'Employee not found!'; - } - else { - $scope.errorMessage = "Error getting Employee!"; - } - }); - } - - $scope.addEmployee = function () { - if ($scope.employee != null && $scope.employee.id) { - EmployeeCRUDService.addEmployee($scope.employee.id, $scope.employee.name, $scope.employee.age) - .then (function success(response){ - $scope.message = 'Employee added!'; - $scope.errorMessage = ''; - }, - function error(response){ - $scope.errorMessage = 'Error adding Employee!'; - $scope.message = ''; - }); - } - else { - $scope.errorMessage = 'Please enter an id!'; - $scope.message = ''; - } - } - - $scope.deleteEmployee = function () { - EmployeeCRUDService.deleteEmployee($scope.employee.id) - .then (function success(response){ - $scope.message = 'Employee deleted!'; - $scope.employee = null; - $scope.errorMessage=''; - }, - function error(response){ - $scope.errorMessage = 'Error deleting Employee!'; - $scope.message=''; - }) - } - - $scope.getAllEmployees = function () { - EmployeeCRUDService.getAllEmployees() - .then(function success(response){ - $scope.employees = response.data._embedded.employee; - $scope.message=''; - $scope.errorMessage = ''; - }, - function error (response ){ - $scope.message=''; - $scope.errorMessage = 'Error getting Employees!'; - }); - } - - $scope.getEmployeesByName = function () { - EmployeeCRUDService.getEmployeesByName($scope.name) - .then(function success(response){ - $scope.employees = response.data._embedded.employee; - $scope.message=''; - $scope.errorMessage = ''; - }, - function error (response ){ - $scope.message=''; - $scope.errorMessage = 'Error getting Employees!'; - }); - } - -}]); - -app.service('EmployeeCRUDService',['$http', function ($http) { - - this.getEmployee = function getEmployee(employeeId){ - return $http({ - method: 'GET', - url:'employees/'+employeeId - }); - } - - this.addEmployee = function addEmployee(id, name, age, gender){ - return $http({ - method: 'POST', - url:'employees', - data: {id:id, name:name, age:age} - }); - } - - this.deleteEmployee = function deleteEmployee(id){ - return $http({ - method: 'DELETE', - url: 'employees/'+id - }) - } - - this.updateEmployee = function updateEmployee(id,name,age){ - return $http({ - method: 'PATCH', - url: 'employees/'+id, - data: {name:name, age:age} - }) - } - - this.getAllEmployees = function getAllEmployees(){ - return $http({ - method: 'GET', - url:'employees' - }); - } - - this.getEmployeesByName = function getEmployeesByName(name){ - return $http({ - method: 'GET', - url:'employees/search/findByName', - params:{name:name} - }); - } - -}]); \ No newline at end of file diff --git a/spring-rest-angular/src/test/java/org/baeldung/web/service/EmployeeCRUDRepositoryIntegrationTest.java b/spring-rest-angular/src/test/java/org/baeldung/web/service/EmployeeCRUDRepositoryIntegrationTest.java deleted file mode 100644 index 57fe793596fe..000000000000 --- a/spring-rest-angular/src/test/java/org/baeldung/web/service/EmployeeCRUDRepositoryIntegrationTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.baeldung.web.service; - -import static org.junit.Assert.*; - -import org.baeldung.web.entity.Employee; -import org.baeldung.web.main.Application; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.test.context.junit4.SpringRunner; - -@RunWith(SpringRunner.class) -@SpringBootTest(classes = Application.class, webEnvironment = WebEnvironment.DEFINED_PORT) -public class EmployeeCRUDRepositoryIntegrationTest { - - @Autowired - private TestRestTemplate template; - - private static final String EMPLOYEE_ENDPOINT = "http://localhost:8080/employees/"; - private static int EMPLOYEE_ID = 1; - private static int EMPLOYEE_AGE = 25; - - @Test - public void whenEmployeeCRUDOperations_thenCorrect() { - Employee Employee = new Employee(EMPLOYEE_ID, "Bryan", 20); - ResponseEntity postResponse = template.postForEntity(EMPLOYEE_ENDPOINT, Employee, Employee.class, ""); - assertEquals("status is not 201", HttpStatus.CREATED, postResponse.getStatusCode()); - - Employee.setAge(EMPLOYEE_AGE); - Employee patchResponse = template.patchForObject(EMPLOYEE_ENDPOINT + "/" + EMPLOYEE_ID, Employee, Employee.class); - assertEquals("age is not 25", Integer.valueOf(EMPLOYEE_AGE), patchResponse.getAge()); - - ResponseEntity getResponse = template.getForEntity(EMPLOYEE_ENDPOINT + "/" + EMPLOYEE_ID, Employee.class, ""); - assertEquals("status is not 200", HttpStatus.OK, getResponse.getStatusCode()); - - template.delete(EMPLOYEE_ENDPOINT + "/" + EMPLOYEE_ID); - - getResponse = template.getForEntity(EMPLOYEE_ENDPOINT + "/" + EMPLOYEE_ID, Employee.class, ""); - assertEquals("status is not 404", HttpStatus.NOT_FOUND, getResponse.getStatusCode()); - - } -} diff --git a/spring-rest/README.md b/spring-rest/README.md index 85f0e1373256..66c893e45f9f 100644 --- a/spring-rest/README.md +++ b/spring-rest/README.md @@ -13,3 +13,5 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring - [Guide to UriComponentsBuilder in Spring](http://www.baeldung.com/spring-uricomponentsbuilder) - [Introduction to FindBugs](http://www.baeldung.com/intro-to-findbugs) - [A Custom Media Type for a Spring REST API](http://www.baeldung.com/spring-rest-custom-media-type) +- [HTTP PUT vs HTTP PATCH in a REST API](http://www.baeldung.com/http-put-patch-difference-spring) +- [Exploring the Spring Boot TestRestTemplate](http://www.baeldung.com/spring-boot-testresttemplate) diff --git a/spring-rest/pom.xml b/spring-rest/pom.xml index da26d8abe91d..9b1b9a28474e 100644 --- a/spring-rest/pom.xml +++ b/spring-rest/pom.xml @@ -29,6 +29,10 @@ org.springframework.boot spring-boot-devtools + + org.springframework.boot + spring-boot-test + @@ -118,10 +122,6 @@ jcl-over-slf4j - - org.slf4j - log4j-over-slf4j - @@ -184,6 +184,12 @@ kryo ${kryo.version} + + + com.jayway.jsonpath + json-path + + @@ -354,7 +360,8 @@ 3.4.1 - + + 2.2.0 diff --git a/spring-rest/src/main/java/com/baeldung/produceimage/Application.java b/spring-rest/src/main/java/com/baeldung/produceimage/Application.java new file mode 100644 index 000000000000..179671d09425 --- /dev/null +++ b/spring-rest/src/main/java/com/baeldung/produceimage/Application.java @@ -0,0 +1,14 @@ +package com.baeldung.produceimage; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.PropertySource; + +@EnableAutoConfiguration +@ComponentScan("com.baeldung.produceimage") +public class Application { + public static void main(final String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/spring-rest/src/main/java/com/baeldung/produceimage/README.md b/spring-rest/src/main/java/com/baeldung/produceimage/README.md new file mode 100644 index 000000000000..acd546598d09 --- /dev/null +++ b/spring-rest/src/main/java/com/baeldung/produceimage/README.md @@ -0,0 +1,3 @@ +### Relevant articles + +- [Returning an Image or a File with Spring](http://www.baeldung.com/spring-controller-return-image-file) diff --git a/spring-rest/src/main/java/com/baeldung/produceimage/controller/DataProducerController.java b/spring-rest/src/main/java/com/baeldung/produceimage/controller/DataProducerController.java new file mode 100644 index 000000000000..6f34bdb9ae54 --- /dev/null +++ b/spring-rest/src/main/java/com/baeldung/produceimage/controller/DataProducerController.java @@ -0,0 +1,38 @@ +package com.baeldung.produceimage.controller; + +import org.apache.commons.io.IOUtils; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.io.IOException; +import java.io.InputStream; + +@Controller +public class DataProducerController { + + @GetMapping("/get-text") + public @ResponseBody String getText() { + return "Hello world"; + } + + @GetMapping(value = "/get-image") + public @ResponseBody byte[] getImage() throws IOException { + final InputStream in = getClass().getResourceAsStream("/com/baeldung/produceimage/image.jpg"); + return IOUtils.toByteArray(in); + } + + @GetMapping(value = "/get-image-with-media-type", produces = MediaType.IMAGE_JPEG_VALUE) + public @ResponseBody byte[] getImageWithMediaType() throws IOException { + final InputStream in = getClass().getResourceAsStream("/com/baeldung/produceimage/image.jpg"); + return IOUtils.toByteArray(in); + } + + @GetMapping(value = "/get-file", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) + public @ResponseBody byte[] getFile() throws IOException { + final InputStream in = getClass().getResourceAsStream("/com/baeldung/produceimage/data.txt"); + return IOUtils.toByteArray(in); + } + +} diff --git a/spring-rest/src/main/java/org/baeldung/repository/HeavyResourceRepository.java b/spring-rest/src/main/java/org/baeldung/repository/HeavyResourceRepository.java new file mode 100644 index 000000000000..5556d85f6524 --- /dev/null +++ b/spring-rest/src/main/java/org/baeldung/repository/HeavyResourceRepository.java @@ -0,0 +1,28 @@ +package org.baeldung.repository; + +import org.baeldung.web.dto.HeavyResource; +import org.baeldung.web.dto.HeavyResourceAddressOnly; +import org.baeldung.web.dto.HeavyResourceAddressPartialUpdate; + +import java.util.Map; + +public class HeavyResourceRepository { + + public void save(HeavyResource heavyResource) { + } + + public void save(HeavyResourceAddressOnly partialUpdate) { + + } + + public void save(Map updates, String id) { + + } + + public void save(HeavyResource heavyResource, String id) { + + } + public void save(HeavyResourceAddressOnly partialUpdate, String id) { + + } +} diff --git a/spring-rest/src/main/java/org/baeldung/web/controller/BazzNewMappingsExampleController.java b/spring-rest/src/main/java/org/baeldung/web/controller/BazzNewMappingsExampleController.java new file mode 100644 index 000000000000..4bcafc04fc8f --- /dev/null +++ b/spring-rest/src/main/java/org/baeldung/web/controller/BazzNewMappingsExampleController.java @@ -0,0 +1,55 @@ +package org.baeldung.web.controller; + +import java.util.Arrays; +import java.util.List; + +import org.baeldung.web.dto.Bazz; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import com.fasterxml.jackson.core.JsonProcessingException; + +@RestController +@RequestMapping("/bazz") +public class BazzNewMappingsExampleController { + + @GetMapping + public ResponseEntity getBazzs() throws JsonProcessingException{ + List data = Arrays.asList( + new Bazz("1", "Bazz1"), + new Bazz("2", "Bazz2"), + new Bazz("3", "Bazz3"), + new Bazz("4", "Bazz4")); + return new ResponseEntity<>(data, HttpStatus.OK); + } + + @GetMapping("/{id}") + public ResponseEntity getBazz(@PathVariable String id){ + return new ResponseEntity<>(new Bazz(id, "Bazz"+id), HttpStatus.OK); + } + + @PostMapping + public ResponseEntity newBazz(@RequestParam("name") String name){ + return new ResponseEntity<>(new Bazz("5", name), HttpStatus.OK); + } + + @PutMapping("/{id}") + public ResponseEntity updateBazz(@PathVariable String id, + @RequestParam("name") String name){ + return new ResponseEntity<>(new Bazz(id, name), HttpStatus.OK); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteBazz(@PathVariable String id){ + return new ResponseEntity<>(new Bazz(id), HttpStatus.OK); + } + +} diff --git a/spring-rest/src/main/java/org/baeldung/web/controller/HeavyResourceController.java b/spring-rest/src/main/java/org/baeldung/web/controller/HeavyResourceController.java new file mode 100644 index 000000000000..55616a64463d --- /dev/null +++ b/spring-rest/src/main/java/org/baeldung/web/controller/HeavyResourceController.java @@ -0,0 +1,37 @@ +package org.baeldung.web.controller; + + +import org.baeldung.repository.HeavyResourceRepository; +import org.baeldung.web.dto.HeavyResource; +import org.baeldung.web.dto.HeavyResourceAddressOnly; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +@RestController +public class HeavyResourceController { + + private HeavyResourceRepository heavyResourceRepository = new HeavyResourceRepository(); + + @RequestMapping(value = "/heavyresource/{id}", method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity saveResource(@RequestBody HeavyResource heavyResource, @PathVariable("id") String id) { + heavyResourceRepository.save(heavyResource, id); + return ResponseEntity.ok("resource saved"); + } + + @RequestMapping(value = "/heavyresource/{id}", method = RequestMethod.PATCH, consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity partialUpdateName(@RequestBody HeavyResourceAddressOnly partialUpdate, @PathVariable("id") String id) { + heavyResourceRepository.save(partialUpdate, id); + return ResponseEntity.ok("resource address updated"); + } + + @RequestMapping(value = "/heavyresource2/{id}", method = RequestMethod.PATCH, consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity partialUpdateGeneric(@RequestBody Map updates, + @PathVariable("id") String id) { + heavyResourceRepository.save(updates, id); + return ResponseEntity.ok("resource updated"); + } + +} diff --git a/spring-rest/src/main/java/org/baeldung/web/dto/Bazz.java b/spring-rest/src/main/java/org/baeldung/web/dto/Bazz.java new file mode 100644 index 000000000000..d9a495b726a1 --- /dev/null +++ b/spring-rest/src/main/java/org/baeldung/web/dto/Bazz.java @@ -0,0 +1,22 @@ +package org.baeldung.web.dto; + +public class Bazz { + + + public String id; + public String name; + + public Bazz(String id){ + this.id = id; + } + public Bazz(String id, String name) { + this.id = id; + this.name = name; + } + + @Override + public String toString() { + return "Bazz [id=" + id + ", name=" + name + "]"; + } + +} diff --git a/spring-rest/src/main/java/org/baeldung/web/dto/HeavyResource.java b/spring-rest/src/main/java/org/baeldung/web/dto/HeavyResource.java new file mode 100644 index 000000000000..934e76606f01 --- /dev/null +++ b/spring-rest/src/main/java/org/baeldung/web/dto/HeavyResource.java @@ -0,0 +1,62 @@ +package org.baeldung.web.dto; + + +public class HeavyResource { + private Integer id; + private String name; + private String surname; + private Integer age; + private String address; + + + public HeavyResource() { + } + + public HeavyResource(Integer id, String name, String surname, Integer age, String address) { + this.id = id; + this.name = name; + this.surname = surname; + this.age = age; + this.address = address; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getSurname() { + return surname; + } + + public void setSurname(String surname) { + this.surname = surname; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } +} diff --git a/spring-rest/src/main/java/org/baeldung/web/dto/HeavyResourceAddressOnly.java b/spring-rest/src/main/java/org/baeldung/web/dto/HeavyResourceAddressOnly.java new file mode 100644 index 000000000000..f96347d60cf4 --- /dev/null +++ b/spring-rest/src/main/java/org/baeldung/web/dto/HeavyResourceAddressOnly.java @@ -0,0 +1,31 @@ +package org.baeldung.web.dto; + + +public class HeavyResourceAddressOnly { + private Integer id; + private String address; + + public HeavyResourceAddressOnly() { + } + + public HeavyResourceAddressOnly(Integer id, String address) { + this.id = id; + this.address = address; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } +} diff --git a/spring-rest/src/main/java/org/baeldung/web/dto/HeavyResourceAddressPartialUpdate.java b/spring-rest/src/main/java/org/baeldung/web/dto/HeavyResourceAddressPartialUpdate.java new file mode 100644 index 000000000000..f90f02ea08fe --- /dev/null +++ b/spring-rest/src/main/java/org/baeldung/web/dto/HeavyResourceAddressPartialUpdate.java @@ -0,0 +1,31 @@ +package org.baeldung.web.dto; + + +public class HeavyResourceAddressPartialUpdate { + private Integer id; + private String address; + + public HeavyResourceAddressPartialUpdate() { + } + + public HeavyResourceAddressPartialUpdate(Integer id, String address) { + this.id = id; + this.address = address; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } +} diff --git a/spring-rest/src/main/resources/com/baeldung/produceimage/data.txt b/spring-rest/src/main/resources/com/baeldung/produceimage/data.txt new file mode 100644 index 000000000000..3cd18170c03c --- /dev/null +++ b/spring-rest/src/main/resources/com/baeldung/produceimage/data.txt @@ -0,0 +1 @@ +This is a sample file containing text data \ No newline at end of file diff --git a/spring-rest/src/main/resources/com/baeldung/produceimage/image.jpg b/spring-rest/src/main/resources/com/baeldung/produceimage/image.jpg new file mode 100644 index 000000000000..0262656a33a0 Binary files /dev/null and b/spring-rest/src/main/resources/com/baeldung/produceimage/image.jpg differ diff --git a/spring-rest/src/main/resources/logback.xml b/spring-rest/src/main/resources/logback.xml index 1146dade632e..ec0dc2469ae0 100644 --- a/spring-rest/src/main/resources/logback.xml +++ b/spring-rest/src/main/resources/logback.xml @@ -1,20 +1,19 @@ + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - + + - - - - - - - - - + + + + + \ No newline at end of file diff --git a/spring-rest/src/test/java/org/baeldung/client/TestRestTemplateBasicLiveTest.java b/spring-rest/src/test/java/org/baeldung/client/TestRestTemplateBasicLiveTest.java new file mode 100644 index 000000000000..a4d8eb0908ed --- /dev/null +++ b/spring-rest/src/test/java/org/baeldung/client/TestRestTemplateBasicLiveTest.java @@ -0,0 +1,117 @@ +package org.baeldung.client; + +import okhttp3.Request; +import okhttp3.RequestBody; +import org.junit.Before; +import org.junit.Test; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestTemplate; + +import static org.baeldung.client.Consts.APPLICATION_PORT; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertTrue; + +public class TestRestTemplateBasicLiveTest { + + private RestTemplate restTemplate; + private static final String FOO_RESOURCE_URL = "http://localhost:" + APPLICATION_PORT + "/spring-rest/myfoos"; + private static final String URL_SECURED_BY_AUTHENTICATION = "http://httpbin.org/basic-auth/user/passwd"; + private static final String BASE_URL = "http://localhost:" + APPLICATION_PORT + "/spring-rest"; + + @Before + public void beforeTest() { + restTemplate = new RestTemplate(); + } + + // GET + @Test + public void givenTestRestTemplate_whenSendGetForEntity_thenStatusOk() { + TestRestTemplate testRestTemplate = new TestRestTemplate(); + ResponseEntity response = testRestTemplate.getForEntity(FOO_RESOURCE_URL + "/1", String.class); + assertThat(response.getStatusCode(), equalTo(HttpStatus.OK)); + } + + @Test + public void givenRestTemplateWrapper_whenSendGetForEntity_thenStatusOk() { + TestRestTemplate testRestTemplate = new TestRestTemplate(restTemplate); + ResponseEntity response = testRestTemplate.getForEntity(FOO_RESOURCE_URL + "/1", String.class); + assertThat(response.getStatusCode(), equalTo(HttpStatus.OK)); + } + + @Test + public void givenRestTemplateBuilderWrapper_whenSendGetForEntity_thenStatusOk() { + RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder(); + restTemplateBuilder.build(); + TestRestTemplate testRestTemplate = new TestRestTemplate(restTemplateBuilder); + ResponseEntity response = testRestTemplate.getForEntity(FOO_RESOURCE_URL + "/1", String.class); + assertThat(response.getStatusCode(), equalTo(HttpStatus.OK)); + } + + @Test + public void givenRestTemplateWrapperWithCredentials_whenSendGetForEntity_thenStatusOk() { + TestRestTemplate testRestTemplate = new TestRestTemplate(restTemplate, "user", "passwd"); + ResponseEntity response = testRestTemplate.getForEntity(URL_SECURED_BY_AUTHENTICATION, + String.class); + assertThat(response.getStatusCode(), equalTo(HttpStatus.OK)); + } + + @Test + public void givenTestRestTemplateWithCredentials_whenSendGetForEntity_thenStatusOk() { + TestRestTemplate testRestTemplate = new TestRestTemplate("user", "passwd"); + ResponseEntity response = testRestTemplate.getForEntity(URL_SECURED_BY_AUTHENTICATION, + String.class); + assertThat(response.getStatusCode(), equalTo(HttpStatus.OK)); + } + + @Test + public void givenTestRestTemplateWithBasicAuth_whenSendGetForEntity_thenStatusOk() { + TestRestTemplate testRestTemplate = new TestRestTemplate(); + ResponseEntity response = testRestTemplate.withBasicAuth("user", "passwd"). + getForEntity(URL_SECURED_BY_AUTHENTICATION, String.class); + assertThat(response.getStatusCode(), equalTo(HttpStatus.OK)); + } + + @Test + public void givenTestRestTemplateWithCredentialsAndEnabledCookies_whenSendGetForEntity_thenStatusOk() { + TestRestTemplate testRestTemplate = new TestRestTemplate("user", "passwd", TestRestTemplate. + HttpClientOption.ENABLE_COOKIES); + ResponseEntity response = testRestTemplate.getForEntity(URL_SECURED_BY_AUTHENTICATION, + String.class); + assertThat(response.getStatusCode(), equalTo(HttpStatus.OK)); + } + + // HEAD + @Test + public void givenFooService_whenCallHeadForHeaders_thenReceiveAllHeaders() { + TestRestTemplate testRestTemplate = new TestRestTemplate(); + final HttpHeaders httpHeaders = testRestTemplate.headForHeaders(FOO_RESOURCE_URL); + assertTrue(httpHeaders.getContentType().includes(MediaType.APPLICATION_JSON)); + } + + // POST + @Test + public void givenService_whenPostForObject_thenCreatedObjectIsReturned() { + TestRestTemplate testRestTemplate = new TestRestTemplate("user", "passwd"); + final RequestBody body = RequestBody.create(okhttp3.MediaType.parse("text/html; charset=utf-8"), + "{\"id\":1,\"name\":\"Jim\"}"); + final Request request = new Request.Builder().url(BASE_URL + "/users/detail").post(body).build(); + testRestTemplate.postForObject(URL_SECURED_BY_AUTHENTICATION, request, String.class); + } + + // PUT + @Test + public void givenService_whenPutForObject_thenCreatedObjectIsReturned() { + TestRestTemplate testRestTemplate = new TestRestTemplate("user", "passwd"); + final RequestBody body = RequestBody.create(okhttp3.MediaType.parse("text/html; charset=utf-8"), + "{\"id\":1,\"name\":\"Jim\"}"); + final Request request = new Request.Builder().url(BASE_URL + "/users/detail").post(body).build(); + testRestTemplate.put(URL_SECURED_BY_AUTHENTICATION, request, String.class); + } + +} diff --git a/spring-rest/src/test/java/org/baeldung/web/controller/HeavyResourceControllerTest.java b/spring-rest/src/test/java/org/baeldung/web/controller/HeavyResourceControllerTest.java new file mode 100644 index 000000000000..a1f9e71bec27 --- /dev/null +++ b/spring-rest/src/test/java/org/baeldung/web/controller/HeavyResourceControllerTest.java @@ -0,0 +1,70 @@ +package org.baeldung.web.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.baeldung.config.WebConfig; +import org.baeldung.web.dto.HeavyResource; +import org.baeldung.web.dto.HeavyResourceAddressOnly; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.test.context.ContextConfiguration; +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.web.context.WebApplicationContext; + +import java.util.HashMap; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = WebConfig.class) +@WebAppConfiguration +public class HeavyResourceControllerTest { + + private MockMvc mockMvc; + + @Autowired + private WebApplicationContext webApplicationContext; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Before + public void setUp() { + mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); + } + + @Test + public void givenHeavyResource_whenSendPutRequest_thenCreateResource() throws Exception { + mockMvc.perform(put("/heavyresource/1") + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(objectMapper.writeValueAsString(new HeavyResource(1, "Tom", "Jackson", 12, "heaven street"))) + ).andExpect(status().isOk()); + } + + @Test + public void givenNewAddressOfResource_whenExecutePatchRequest_thenUpdateResourcePartially() throws Exception { + mockMvc.perform(patch("/heavyresource/1") + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(objectMapper.writeValueAsString(new HeavyResourceAddressOnly(1, "5th avenue"))) + ).andExpect(status().isOk()); + } + + @Test + public void givenNewAddressOfResource_whenExecutePatchGeneric_thenUpdateResourcePartially() throws Exception { + HashMap updates = new HashMap<>(); + updates.put("address", "5th avenue"); + + mockMvc.perform(patch("/heavyresource/1") + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(objectMapper.writeValueAsString(updates)) + ).andExpect(status().isOk()); + } + +} \ No newline at end of file diff --git a/spring-rest/src/test/java/org/baeldung/web/test/BazzNewMappingsExampleControllerTest.java b/spring-rest/src/test/java/org/baeldung/web/test/BazzNewMappingsExampleControllerTest.java new file mode 100644 index 000000000000..f2f00a40d88a --- /dev/null +++ b/spring-rest/src/test/java/org/baeldung/web/test/BazzNewMappingsExampleControllerTest.java @@ -0,0 +1,80 @@ + +package org.baeldung.web.test; + +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +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.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.baeldung.config.WebConfig; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = WebConfig.class) +@WebAppConfiguration +public class BazzNewMappingsExampleControllerTest { + + private MockMvc mockMvc; + + @Autowired + private WebApplicationContext webApplicationContext; + + @Before + public void setUp() { + mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); + } + + @Test + public void whenGettingAllBazz_thenSuccess() throws Exception{ + mockMvc.perform(get("/bazz")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(4))) + .andExpect(jsonPath("$[1].id", is("2"))) + .andExpect(jsonPath("$[1].name", is("Bazz2"))); + } + + @Test + public void whenGettingABazz_thenSuccess() throws Exception{ + mockMvc.perform(get("/bazz/1")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id", is("1"))) + .andExpect(jsonPath("$.name", is("Bazz1"))); + } + + @Test + public void whenAddingABazz_thenSuccess() throws Exception{ + mockMvc.perform(post("/bazz").param("name", "Bazz5")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id", is("5"))) + .andExpect(jsonPath("$.name", is("Bazz5"))); + } + + @Test + public void whenUpdatingABazz_thenSuccess() throws Exception{ + mockMvc.perform(put("/bazz/5").param("name", "Bazz6")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id", is("5"))) + .andExpect(jsonPath("$.name", is("Bazz6"))); + } + + @Test + public void whenDeletingABazz_thenSuccess() throws Exception{ + mockMvc.perform(delete("/bazz/5")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id", is("5"))); + } +} diff --git a/spring-rest/src/test/resources/cache/2d9345a30d2cc31bb3091d70a8ef6c18.0 b/spring-rest/src/test/resources/cache/2d9345a30d2cc31bb3091d70a8ef6c18.0 new file mode 100644 index 000000000000..78d5d3fcf3d3 --- /dev/null +++ b/spring-rest/src/test/resources/cache/2d9345a30d2cc31bb3091d70a8ef6c18.0 @@ -0,0 +1,24 @@ +https://publicobject.com/helloworld.txt +GET +0 +HTTP/1.1 200 OK +10 +Server: nginx/1.10.0 (Ubuntu) +Date: Thu, 09 Mar 2017 10:17:25 GMT +Content-Type: text/plain +Content-Length: 1759 +Last-Modified: Tue, 27 May 2014 02:35:47 GMT +Connection: keep-alive +ETag: "5383fa03-6df" +Accept-Ranges: bytes +OkHttp-Sent-Millis: 1489054646765 +OkHttp-Received-Millis: 1489054646966 + +TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 +4 +MIIFVTCCBD2gAwIBAgIRAKgHBM+t9Yx3v9G9tGZECWkwDQYJKoZIhvcNAQELBQAwgZAxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMTYwNAYDVQQDEy1DT01PRE8gUlNBIERvbWFpbiBWYWxpZGF0aW9uIFNlY3VyZSBTZXJ2ZXIgQ0EwHhcNMTQxMDExMDAwMDAwWhcNMTkxMDEwMjM1OTU5WjBUMSEwHwYDVQQLExhEb21haW4gQ29udHJvbCBWYWxpZGF0ZWQxFDASBgNVBAsTC1Bvc2l0aXZlU1NMMRkwFwYDVQQDExBwdWJsaWNvYmplY3QuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjtgQtvL2kUr6ooHMOq7cxQLGycBW+ri9TGyQkO1lTb66RmcAujENxMh51wKodrveUdbqwpL4g1P49o/Y1fK5IHWAf3vpE8p3RyELY0NRlclRM24dgif/+dgRUUk+0kF3NH6MbB/kve07FMF2FyNDLxtbwJvmrn1MI5c52cpxI24vGcpOZ0VIW7+nS3V+QSrEinvrugAtu8b6Gpg+I8w6rAvmjpfCLmLP0zbjz5ExJzMC0TnR6JMgiqo2TUIyuDM2OuNJpyiluNvlUnzFrlRieg7xexoJxCbqqiOSm076fdT9qNzBp+4MzQ8w8Ofm8tsOnM4FNsz3ifX6KpJdIXfsAQIDAQABo4IB4zCCAd8wHwYDVR0jBBgwFoAUkK9qOpRaC9iQ6hJWc99DtDoo2ucwHQYDVR0OBBYEFAmSn3icQLzlRnBujuf7Y+i7/6HbMA4GA1UdDwEB/wQEAwIFoDAMBgNVHRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBPBgNVHSAESDBGMDoGCysGAQQBsjEBAgIHMCswKQYIKwYBBQUHAgEWHWh0dHBzOi8vc2VjdXJlLmNvbW9kby5jb20vQ1BTMAgGBmeBDAECATBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9SU0FEb21haW5WYWxpZGF0aW9uU2VjdXJlU2VydmVyQ0EuY3JsMIGFBggrBgEFBQcBAQR5MHcwTwYIKwYBBQUHMAKGQ2h0dHA6Ly9jcnQuY29tb2RvY2EuY29tL0NPTU9ET1JTQURvbWFpblZhbGlkYXRpb25TZWN1cmVTZXJ2ZXJDQS5jcnQwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmNvbW9kb2NhLmNvbTAxBgNVHREEKjAoghBwdWJsaWNvYmplY3QuY29tghR3d3cucHVibGljb2JqZWN0LmNvbTANBgkqhkiG9w0BAQsFAAOCAQEATWNaqr7WgumGhxmAm7yluVhVZ/pxPabACY4HDLrYN61KB7XgI1PZIJhQkkreBtmDLIkOQqJxbhePp3z/nOil0QJT7ONcdnYBX0CO8xYhf8c0FM9z7XbLBLta1pkTF/bwgK3VUsGYOskyQ3YdTUrmZq5WrYJvdbP2G5F5eEVIHnXvjKcdFpEY5CmZagYPwVtSioiup+xUzrBibJxpOD9fB6GV8okLgVjIl29Hs1zC++9o3yWC3ep1qzU+m59Pwi7uPoqUA0LXHi4iIEUk8fRhkNlhkte9geOne+fVvm/Rj9MZD3Gtb5qKoqEld6bOSoMlYavj9GCBSNIx2+mGS0Tg6A== +MIIGCDCCA/CgAwIBAgIQKy5u6tl1NmwUim7bo3yMBzANBgkqhkiG9w0BAQwFADCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTQwMjEyMDAwMDAwWhcNMjkwMjExMjM1OTU5WjCBkDELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxNjA0BgNVBAMTLUNPTU9ETyBSU0EgRG9tYWluIFZhbGlkYXRpb24gU2VjdXJlIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAI7CAhnhoFmk6zg1jSz9AdDTScBkxwtiBUUWOqigwAwCfx3M28ShbXcDow+G+eMGnD4LgYqbSRutA776S9uMIO3Vzl5ljj4Nr0zCsLdFXlIvNN5IJGS0Qa4Al/e+Z96e0HqnU4A7fK31llVvl0cKfIWLIpeNs4TgllfQcBhglo/uLQeTnaG6ytHNe+nEKpooIZFNb5JPJaXyejXdJtxGpdCsWTWM/06RQ1A/WZMebFEh7lgUq/51UHg+TLAchhP6a5i84DuUHoVS3AOTJBhuyydRReZw3iVDpA3hSqXttn7IzW3uLh0nc13cRTCAquOyQQuvvUSH2rnlG51/ruWFgqUCAwEAAaOCAWUwggFhMB8GA1UdIwQYMBaAFLuvfgI9+qbxPISOre44mOzZMjLUMB0GA1UdDgQWBBSQr2o6lFoL2JDqElZz30O0Oija5zAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwGwYDVR0gBBQwEjAGBgRVHSAAMAgGBmeBDAECATBMBgNVHR8ERTBDMEGgP6A9hjtodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9SU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDBxBggrBgEFBQcBAQRlMGMwOwYIKwYBBQUHMAKGL2h0dHA6Ly9jcnQuY29tb2RvY2EuY29tL0NPTU9ET1JTQUFkZFRydXN0Q0EuY3J0MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wDQYJKoZIhvcNAQEMBQADggIBAE4rdk+SHGI2ibp3wScF9BzWRJ2pmj6q1WZmAT7qSeaiNbz69t2Vjpk1mA42GHWx3d1Qcnyu3HeIzg/3kCDKo2cuH1Z/e+FE6kKVxF0NAVBGFfKBiVlsit2M8RKhjTpCipj4SzR7JzsItG8kO3KdY3RYPBpsP0/HEZrIqPW1N+8QRcZs2eBelSaz662jue5/DJpmNXMyYE7l3YphLG5SEXdoltMYdVEVABt0iN3hxzgEQyjpFv3ZBdRdRydg1vs4O2xyopT4Qhrf7W8GjEXCBgCq5Ojc2bXhc3js9iPc0d1sjhqPpepUfJa3w/5Vjo1JXvxku88+vZbrac2/4EjxYoIQ5QxGV/Iz2tDIY+3GH5QFlkoakdH368+PUq4NCNk+qKBR6cGHdNXJ93SrLlP7u3r7l+L4HyaPs9Kg4DdbKDsx5Q5XLVq4rXmsXiBmGqW5prU5wfWYQ//u+aen/e7KJD2AFsQXj4rBYKEMrltDR5FL1ZoXX/nUh8HCjLfn4g8wGTeGrODcQgPmlKidrv0PJFGUzpII0fxQ8ANAe4hZ7Q7drNJ3gjTcBpUC2JD5Leo31Rpg0Gcg19hCC0Wvgmje3WYkN5AplBlGGSW4gNfL1IYoakRwJiNiqZ+Gb7+6kHDSVneFeO/qJakXzlByjAA6quPbYzSf+AZxAeKCINT+b72x +MIIFdDCCBFygAwIBAgIQJ2buVutJ846r13Ci/ITeIjANBgkqhkiG9w0BAQwFADBvMQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowgYUxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMSswKQYDVQQDEyJDT01PRE8gUlNBIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAkehUktIKVrGsDSTdxc9EZ3SZKzejfSNwAHG8U9/E+ioSj0t/EFa9n3Byt2F/yUsPF6c947AEYe7/EZfH9IY+Cvo+XPmT5jR62RRr55yzhaCCenavcZDX7P0N+pxs+t+wgvQUfvm+xKYvT3+Zf7X8Z0NyvQwA1onrayzT7Y+YHBSrfuXjbvzYqOSSJNpDa2K4Vf3qwbxstovzDo2a5JtsaZn4eEgwRdWt4Q08RWD8MpZRJ7xnw8outmvqRsfHIKCxH2XeSAi6pE6p8oNGN4Tr6MyBSENnTnIqm1y9TBsoilwie7SrmNnu4FGDwwlGTm0+mfqVF9p8M1dBPI1R7Qu2XK8sYxrfV8g/vOldxJuvRZnio1oktLqpVj3Pb6r/SVi+8Kj/9Lit6Tf7urj0Czr56ENCHonYhMsT8dm74YlguIwoVqwUHZwK53Hrzw7dPamWoUi9PPevtQ0iTMARgexWO/bTouJbt7IEIlKVgJNp6I5MZfGRAy1wdALqi2cVKWlSArvX31BqVUa/oKMoYX9w0MOiqiwhqkfOKJwGRXa/ghgntNWutMtQ5mv0TIZxMOmm3xaG4Nj/QN370EKIf6MzOi5cHkERgWPOGHFrK+ymircxXDpqR+DDeVnWIBqv8mqYqnK8V0rSS527EPywTEHl7R09XiidnMy/s1Hap0flhFMCAwEAAaOB9DCB8TAfBgNVHSMEGDAWgBStvZh6NLQm9/rEJlTvA73gJMtUGjAdBgNVHQ4EFgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0gBAowCDAGBgRVHSAAMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9BZGRUcnVzdEV4dGVybmFsQ0FSb290LmNybDA1BggrBgEFBQcBAQQpMCcwJQYIKwYBBQUHMAGGGWh0dHA6Ly9vY3NwLnVzZXJ0cnVzdC5jb20wDQYJKoZIhvcNAQEMBQADggEBAGS/g/FfmoXQzbihKVcN6Fr30ek+8nYEbvFScLsePP9NDXRqzIGCJdPDoCpdTPW6i6FtxFQJdcfjJw5dhHk3QBN39bSsHNA7qxcS1u80GH4r6XnTq1dFDK8o+tDb5VCViLvfhVdpfZLYUspzgb8c8+a4bmYRBbMelC1/kZWSWfFMzqORcUx8Rww7Cxn2obFshj5cqsQugsv5B5a6SE2Q8pTIqXOi6wZ7I53eovNNVZ96YUWYGGjHXkBrI/V5eu+MtWuLt29G9HvxPUsE2JOAWVrgQSQdso8VYFhH2+9uRv0V9dlfmrPb2LjkQLPNlzmuhbsdjrzch5vRpu/xO28QOG8= +MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5hbCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvtH7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzXmk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LXa0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzNE0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYDVR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxHYINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw56wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvCNr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEXc4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5amnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= +0 +TLSv1.2 diff --git a/spring-rest/src/test/resources/cache/2d9345a30d2cc31bb3091d70a8ef6c18.1 b/spring-rest/src/test/resources/cache/2d9345a30d2cc31bb3091d70a8ef6c18.1 new file mode 100644 index 000000000000..83db2312f032 --- /dev/null +++ b/spring-rest/src/test/resources/cache/2d9345a30d2cc31bb3091d70a8ef6c18.1 @@ -0,0 +1,39 @@ + + \\ // + \\ .ooo. // + .@@@@@@@@@. + :@@@@@@@@@@@@@: + :@@. '@@@@@' .@@: + @@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@ + + :@@ :@@@@@@@@@@@@@@@@@. @@: + @@@ '@@@@@@@@@@@@@@@@@, @@@ + @@@ '@@@@@@@@@@@@@@@@@, @@@ + @@@ '@@@@@@@@@@@@@@@@@, @@@ + @@@ '@@@@@@@@@@@@@@@@@, @@@ + @@@ '@@@@@@@@@@@@@@@@@, @@@ + @@@ '@@@@@@@@@@@@@@@@@, @@@ + @@@@@@@@@@@@@@@@@ + '@@@@@@@@@@@@@@@' + @@@@ @@@@ + @@@@ @@@@ + @@@@ @@@@ + '@@' '@@' + + :@@@. + .@@@@@@@: +@@ `@@ @@` @@ @@ + .@@@@'@@@@: +@@ `@@ @@` @@ @@ + @@@ @@@ +@@ `@@ @@` @@ @@ + .@@ @@: +@@ @@@ `@@ @@` @@@@@@ @@@@@@ @@;@@@@@ + @@@ @@@ +@@ @@@ `@@ @@` @@@@@@ @@@@@@ @@@@@@@@@ + @@@ @@@ +@@ @@@ `@@@@@@@@@@` @@ @@ @@@ :@@ + @@@ @@@ +@@@@@ `@@@@@@@@@@` @@ @@ @@# @@+ + @@@ @@@ +@@@@@+ `@@ @@` @@ @@ @@: @@# + @@: .@@` +@@@+@@ `@@ @@` @@ @@ @@# @@+ + @@@. .@@@ +@@ @@@ `@@ @@` @@ @@ @@@ ,@@ + @@@@@@@@@ +@@ @@@ `@@ @@` @@@@ @@@@ @@@@#@@@@ + @@@@@@@ +@@ #@@ `@@ @@` @@@@: @@@@: @@'@@@@@ + @@: + @@: + @@: diff --git a/spring-rest/src/test/resources/cache/4b217e04ba52215f3a6b64d28f6729c6.0 b/spring-rest/src/test/resources/cache/4b217e04ba52215f3a6b64d28f6729c6.0 new file mode 100644 index 000000000000..82c93f0a869b --- /dev/null +++ b/spring-rest/src/test/resources/cache/4b217e04ba52215f3a6b64d28f6729c6.0 @@ -0,0 +1,13 @@ +http://publicobject.com/helloworld.txt +GET +0 +HTTP/1.1 301 Moved Permanently +8 +Server: nginx/1.10.0 (Ubuntu) +Date: Thu, 09 Mar 2017 10:17:25 GMT +Content-Type: text/html +Content-Length: 194 +Connection: keep-alive +Location: https://publicobject.com/helloworld.txt +OkHttp-Sent-Millis: 1489054646977 +OkHttp-Received-Millis: 1489054647185 diff --git a/spring-rest/src/test/resources/cache/4b217e04ba52215f3a6b64d28f6729c6.1 b/spring-rest/src/test/resources/cache/4b217e04ba52215f3a6b64d28f6729c6.1 new file mode 100644 index 000000000000..acf72eabe3bd --- /dev/null +++ b/spring-rest/src/test/resources/cache/4b217e04ba52215f3a6b64d28f6729c6.1 @@ -0,0 +1,7 @@ + +301 Moved Permanently + +

301 Moved Permanently

+
nginx/1.10.0 (Ubuntu)
+ + diff --git a/spring-rest/src/test/resources/cache/journal b/spring-rest/src/test/resources/cache/journal new file mode 100644 index 000000000000..44b709c1798e --- /dev/null +++ b/spring-rest/src/test/resources/cache/journal @@ -0,0 +1,12 @@ +libcore.io.DiskLruCache +1 +201105 +2 + +DIRTY 4b217e04ba52215f3a6b64d28f6729c6 +CLEAN 4b217e04ba52215f3a6b64d28f6729c6 333 194 +DIRTY 2d9345a30d2cc31bb3091d70a8ef6c18 +CLEAN 2d9345a30d2cc31bb3091d70a8ef6c18 7618 1759 +READ 4b217e04ba52215f3a6b64d28f6729c6 +DIRTY 4b217e04ba52215f3a6b64d28f6729c6 +CLEAN 4b217e04ba52215f3a6b64d28f6729c6 333 194 diff --git a/spring-roo/pom.xml b/spring-roo/pom.xml new file mode 100644 index 000000000000..e1d69a303135 --- /dev/null +++ b/spring-roo/pom.xml @@ -0,0 +1,644 @@ + + + + + io.spring.platform + platform-bom + Athens-RELEASE + + + 4.0.0 + com.baeldung + roo + 1.0.0.BUILD-SNAPSHOT + roo + + jar + + + 2.0.0.RC1 + 8 + UTF-8 + 1.8 + 1.5.4 + 1.4.1.RELEASE + 1.8 + 1.2.0.RC1 + 1.1.2 + 3.0.0.RELEASE + 2.0.0 + 2.0.1 + 5.0.11 + 1.0 + 3.3.6 + 1.10.12 + 1.10.11 + 1.1.2 + 1.1.2 + 2.0.2 + 2.0.2 + 1.1.2 + 1.1.2 + 1.1.2 + 2.0.0 + 2.5.4 + 4.6.2 + 1.12.3 + 3.3.1 + 1.15.0 + 2.13.0 + 4.0.3 + 0.1.0-beta.7 + 1.4.2 + 3.7.3 + 1.0.3 + + + + + + maven-snapshot-repository + Maven Snapshot Repository + https://oss.sonatype.org/content/repositories/snapshots + + false + + + true + + + + + spring-roo-repository + Spring Roo Repository + http://repo.spring.io/spring-roo + + + + + + + + + org.springframework.roo + org.springframework.roo.annotations + ${roo.version} + pom + provided + + + + io.springlets + springlets-data-jpa + ${springlets.version} + + + io.springlets + springlets-data-commons + ${springlets.version} + + + io.springlets + springlets-context + ${springlets.version} + + + org.springframework.roo + org.springframework.roo.querydsl.processor + 2.0.0.RELEASE + + + io.tracee.binding + tracee-springmvc + ${tracee.version} + + + io.springlets + springlets-boot-starter-web + ${springlets.version} + + + com.github.mxab.thymeleaf.extras + thymeleaf-extras-data-attribute + ${thymeleaf-data-dialect.version} + + + ar.com.fdvs + DynamicJasper + ${dynamicjasper.version} + + + ar.com.fdvs + DynamicJasper-core-fonts + ${dynamicjasper-fonts.version} + + + org.webjars.bower + bootstrap + ${bootstrap.version} + + + org.webjars.bower + datatables + ${datatables.version} + + + org.webjars.bower + datatables.net-bs + ${datatables-bs.version} + + + org.webjars.bower + datatables.net-buttons + ${datatables-buttons.version} + + + org.webjars.bower + datatables.net-buttons-bs + ${datatables-buttons-bs.version} + + + org.webjars.bower + datatables.net-responsive + ${datatables-responsive.version} + + + org.webjars.bower + datatables.net-responsive-bs + ${datatables-responsive-bs.version} + + + org.webjars.bower + datatables.net-select + ${datatables-select.version} + + + org.webjars.bower + datatables.net-select-bs + ${datatables-select-bs.version} + + + org.webjars.npm + jquery-datatables-checkboxes + ${datatables-checkboxes.version} + + + org.webjars.npm + jquery + + + org.webjars.npm + datatables.net + + + + + org.webjars.bower + github-com-julmot-datatables-mark-js + ${datatables-mark.version} + + + org.webjars.bower + datetimepicker + ${datetimepicker.version} + + + org.webjars.bower + font-awesome + ${fontawesome.version} + + + org.webjars.bower + jquery + ${jquery.version} + + + org.webjars + jquery.inputmask + ${jquery-inputmask.version} + + + org.webjars + jquery + + + + + org.webjars.bower + jquery-validation + ${jquery-validation.version} + + + org.webjars.bower + momentjs + ${momentjs.version} + + + org.webjars.bower + select2 + ${select2.version} + + + org.webjars.bower + select2-bootstrap-theme + ${select2-bootstrap-theme.version} + + + org.webjars + respond + ${respond.version} + + + org.webjars + html5shiv + ${html5shiv.version} + + + org.webjars.bower + ie10-viewport-bug-workaround + ${bootstrap.ie10-viewport-bug-workaround.version} + + + + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.boot + spring-boot-devtools + true + + + + + org.springframework.roo + org.springframework.roo.annotations + pom + + + + + org.aspectj + aspectjrt + + + + + org.apache.commons + commons-lang3 + + + + + org.assertj + assertj-core + + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-jdbc + provided + + + org.hsqldb + hsqldb + provided + + + io.springlets + springlets-data-jpa + + + io.springlets + springlets-data-commons + + + io.springlets + springlets-context + + + javax.validation + validation-api + + + com.querydsl + querydsl-jpa + + + org.springframework.roo + org.springframework.roo.querydsl.processor + + + org.springframework.boot + spring-boot-starter-web + + + joda-time + joda-time + + + io.tracee.binding + tracee-springmvc + + + io.springlets + springlets-boot-starter-web + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + nz.net.ultraq.thymeleaf + thymeleaf-layout-dialect + + + com.github.mxab.thymeleaf.extras + thymeleaf-extras-data-attribute + + + ar.com.fdvs + DynamicJasper + + + ar.com.fdvs + DynamicJasper-core-fonts + + + org.apache.poi + poi + + + org.springframework + spring-context-support + + + org.webjars + webjars-locator + + + org.webjars.bower + bootstrap + + + org.webjars.bower + datatables + + + org.webjars.bower + datatables.net-bs + + + org.webjars.bower + datatables.net-buttons + + + org.webjars.bower + datatables.net-buttons-bs + + + org.webjars.bower + datatables.net-responsive + + + org.webjars.bower + datatables.net-responsive-bs + + + org.webjars.bower + datatables.net-select + + + org.webjars.bower + datatables.net-select-bs + + + org.webjars.npm + jquery-datatables-checkboxes + + + org.webjars.bower + github-com-julmot-datatables-mark-js + + + org.webjars.bower + datetimepicker + + + org.webjars.bower + font-awesome + + + org.webjars.bower + jquery + + + org.webjars + jquery.inputmask + + + org.webjars.bower + jquery-validation + + + org.webjars.bower + momentjs + + + org.webjars.bower + select2 + + + org.webjars.bower + select2-bootstrap-theme + + + org.webjars + respond + + + org.webjars + html5shiv + + + org.webjars.bower + ie10-viewport-bug-workaround + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + ${start-class} + exec + true + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + false + + + + + org.codehaus.mojo + aspectj-maven-plugin + ${aspectj.plugin.version} + + ${java.version} + ${java.version} + ignore + ${java.version} + UTF-8 + + + + process-sources + + compile + test-compile + + + + + + org.aspectj + aspectjtools + ${aspectj.version} + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*_Roo_* + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + --base-dir ${project.basedir} + --attribute + "projectName=${project.name}" + --attribute + "projectVersion=${project.version}" + --attribute "doctype=book" + + + ${project.groupId}:* + + org.asciidoctor.Asciidoclet + + org.asciidoctor + asciidoclet + ${asciidoclet.version} + + true + true + + http://docs.oracle.com/javase/${java.version}/docs/api/ + http://docs.oracle.com/javaee/${java.product.version}/api/ + http://docs.spring.io/spring-framework/docs/${spring.version}/javadoc-api/ + http://docs.spring.io/spring-boot/docs/${spring-boot.version}/api/ + http://fasterxml.github.io/jackson-core/javadoc/2.8/ + http://fasterxml.github.io/jackson-databind/javadoc/2.8 + http://cxf.apache.org/javadoc/latest-3.1.x/ + + src/main/java/overview.adoc + private + ${java.version} + + + + com.mysema.maven + apt-maven-plugin + 1.1.3 + + + + process + + + target/generated-sources/java + + org.springframework.roo.querydsl.processor.RooAnnotationProcessor + + + + + + + + com.querydsl + querydsl-apt + ${querydsl.version} + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.codehaus.mojo + aspectj-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + + com.mysema.maven + apt-maven-plugin + + + + + diff --git a/spring-roo/src/main/java/com/baeldung/RooApplication.java b/spring-roo/src/main/java/com/baeldung/RooApplication.java new file mode 100644 index 000000000000..96f5a4ccc20e --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/RooApplication.java @@ -0,0 +1,23 @@ +package com.baeldung; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * = RooApplication + * + * TODO Auto-generated class documentation + * + */ +@SpringBootApplication +public class RooApplication { + + /** + * TODO Auto-generated method documentation + * + * @param args + */ + public static void main(String[] args) { + SpringApplication.run(RooApplication.class, args); + } +} \ No newline at end of file diff --git a/spring-roo/src/main/java/com/baeldung/config/SpringDataJpaDetachableRepositoryConfiguration.java b/spring-roo/src/main/java/com/baeldung/config/SpringDataJpaDetachableRepositoryConfiguration.java new file mode 100644 index 000000000000..98916a9412c2 --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/config/SpringDataJpaDetachableRepositoryConfiguration.java @@ -0,0 +1,11 @@ +package com.baeldung.config; +import org.springframework.roo.addon.layers.repository.jpa.annotations.RooJpaRepositoryConfiguration; + +/** + * = SpringDataJpaDetachableRepositoryConfiguration + TODO Auto-generated class documentation + * + */ +@RooJpaRepositoryConfiguration +public class SpringDataJpaDetachableRepositoryConfiguration { +} diff --git a/spring-roo/src/main/java/com/baeldung/config/SpringDataJpaDetachableRepositoryConfiguration_Roo_Jpa_Repository_Configuration.aj b/spring-roo/src/main/java/com/baeldung/config/SpringDataJpaDetachableRepositoryConfiguration_Roo_Jpa_Repository_Configuration.aj new file mode 100644 index 000000000000..7f54997ddb14 --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/config/SpringDataJpaDetachableRepositoryConfiguration_Roo_Jpa_Repository_Configuration.aj @@ -0,0 +1,18 @@ +// WARNING: DO NOT EDIT THIS FILE. THIS FILE IS MANAGED BY SPRING ROO. +// You may push code into the target .java compilation unit if you wish to edit any member(s). + +package com.baeldung.config; + +import com.baeldung.RooApplication; +import com.baeldung.config.SpringDataJpaDetachableRepositoryConfiguration; +import io.springlets.data.jpa.repository.support.DetachableJpaRepositoryImpl; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; + +privileged aspect SpringDataJpaDetachableRepositoryConfiguration_Roo_Jpa_Repository_Configuration { + + declare @type: SpringDataJpaDetachableRepositoryConfiguration: @Configuration; + + declare @type: SpringDataJpaDetachableRepositoryConfiguration: @EnableJpaRepositories(repositoryBaseClass = DetachableJpaRepositoryImpl.class, basePackageClasses = RooApplication.class); + +} diff --git a/spring-roo/src/main/java/com/baeldung/config/WebMvcConfiguration.java b/spring-roo/src/main/java/com/baeldung/config/WebMvcConfiguration.java new file mode 100644 index 000000000000..5276300091b9 --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/config/WebMvcConfiguration.java @@ -0,0 +1,13 @@ +package com.baeldung.config; +import org.springframework.roo.addon.web.mvc.controller.annotations.config.RooWebMvcConfiguration; +import org.springframework.roo.addon.web.mvc.thymeleaf.annotations.RooWebMvcThymeleafUIConfiguration; + +/** + * = WebMvcConfiguration + TODO Auto-generated class documentation + * + */ +@RooWebMvcConfiguration(defaultLanguage = "en") +@RooWebMvcThymeleafUIConfiguration +public class WebMvcConfiguration { +} diff --git a/spring-roo/src/main/java/com/baeldung/config/WebMvcConfiguration_Roo_ThymeleafUIConfiguration.aj b/spring-roo/src/main/java/com/baeldung/config/WebMvcConfiguration_Roo_ThymeleafUIConfiguration.aj new file mode 100644 index 000000000000..c21cb6b68cb3 --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/config/WebMvcConfiguration_Roo_ThymeleafUIConfiguration.aj @@ -0,0 +1,110 @@ +// WARNING: DO NOT EDIT THIS FILE. THIS FILE IS MANAGED BY SPRING ROO. +// You may push code into the target .java compilation unit if you wish to edit any member(s). + +package com.baeldung.config; + +import com.baeldung.config.WebMvcConfiguration; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.annotation.Bean; +import org.thymeleaf.TemplateEngine; +import org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver; +import org.thymeleaf.spring4.view.ThymeleafViewResolver; +import org.thymeleaf.templatemode.TemplateMode; + +privileged aspect WebMvcConfiguration_Roo_ThymeleafUIConfiguration { + + declare parents: WebMvcConfiguration implements ApplicationContextAware; + + /** + * TODO Auto-generated attribute documentation + * + */ + @Autowired + private ThymeleafProperties WebMvcConfiguration.thymeleafProperties; + + /** + * TODO Auto-generated attribute documentation + * + */ + @Autowired + private TemplateEngine WebMvcConfiguration.templateEngine; + + /** + * TODO Auto-generated attribute documentation + * + */ + private ApplicationContext WebMvcConfiguration.applicationContext; + + /** + * TODO Auto-generated method documentation + * + * @return ThymeleafProperties + */ + public ThymeleafProperties WebMvcConfiguration.getThymeleafProperties() { + return thymeleafProperties; + } + + /** + * TODO Auto-generated method documentation + * + * @return TemplateEngine + */ + public TemplateEngine WebMvcConfiguration.getTemplateEngine() { + return templateEngine; + } + + /** + * TODO Auto-generated method documentation + * + * @return ApplicationContext + */ + public ApplicationContext WebMvcConfiguration.getApplicationContext() { + return applicationContext; + } + + /** + * TODO Auto-generated method documentation + * + * @param applicationContext + */ + public void WebMvcConfiguration.setApplicationContext(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + /** + * TODO Auto-generated method documentation + * + * @return ThymeleafViewResolver + */ + @Bean + public ThymeleafViewResolver WebMvcConfiguration.javascriptThymeleafViewResolver() { + ThymeleafViewResolver resolver = new ThymeleafViewResolver(); + resolver.setTemplateEngine(getTemplateEngine()); + resolver.setCharacterEncoding("UTF-8"); + resolver.setContentType("application/javascript"); + resolver.setViewNames(new String[] {"*.js"}); + resolver.setCache(getThymeleafProperties().isCache()); + return resolver; + } + + /** + * TODO Auto-generated method documentation + * + * @return SpringResourceTemplateResolver + */ + @Bean + public SpringResourceTemplateResolver WebMvcConfiguration.javascriptTemplateResolver() { + SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver(); + resolver.setApplicationContext(getApplicationContext()); + resolver.setPrefix("classpath:/templates/fragments/js/"); + resolver.setTemplateMode(TemplateMode.JAVASCRIPT); + resolver.setCharacterEncoding("UTF-8"); + resolver.setCheckExistence(true); + resolver.setCacheable(getThymeleafProperties().isCache()); + return resolver; + } + +} diff --git a/spring-roo/src/main/java/com/baeldung/config/WebMvcConfiguration_Roo_WebMvcConfiguration.aj b/spring-roo/src/main/java/com/baeldung/config/WebMvcConfiguration_Roo_WebMvcConfiguration.aj new file mode 100644 index 000000000000..8031c83ca3b3 --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/config/WebMvcConfiguration_Roo_WebMvcConfiguration.aj @@ -0,0 +1,72 @@ +// WARNING: DO NOT EDIT THIS FILE. THIS FILE IS MANAGED BY SPRING ROO. +// You may push code into the target .java compilation unit if you wish to edit any member(s). + +package com.baeldung.config; + +import com.baeldung.config.WebMvcConfiguration; +import io.tracee.binding.springmvc.TraceeInterceptor; +import java.lang.Override; +import java.util.Locale; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; +import org.springframework.web.servlet.LocaleResolver; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; +import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; +import org.springframework.web.servlet.i18n.SessionLocaleResolver; + +privileged aspect WebMvcConfiguration_Roo_WebMvcConfiguration { + + declare parents: WebMvcConfiguration extends WebMvcConfigurerAdapter; + + declare @type: WebMvcConfiguration: @Configuration; + + /** + * TODO Auto-generated method documentation + * + * @return LocalValidatorFactoryBean + */ + @Primary + @Bean + public LocalValidatorFactoryBean WebMvcConfiguration.validator() { + return new LocalValidatorFactoryBean(); + } + + /** + * TODO Auto-generated method documentation + * + * @return LocaleResolver + */ + @Bean + public LocaleResolver WebMvcConfiguration.localeResolver() { + SessionLocaleResolver localeResolver = new SessionLocaleResolver(); + localeResolver.setDefaultLocale(new Locale("en")); + return localeResolver; + } + + /** + * TODO Auto-generated method documentation + * + * @return LocaleChangeInterceptor + */ + @Bean + public LocaleChangeInterceptor WebMvcConfiguration.localeChangeInterceptor() { + LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor(); + localeChangeInterceptor.setParamName("lang"); + return localeChangeInterceptor; + } + + /** + * TODO Auto-generated method documentation + * + * @param registry + */ + @Override + public void WebMvcConfiguration.addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(localeChangeInterceptor()); + registry.addInterceptor(new TraceeInterceptor()); + } + +} diff --git a/spring-roo/src/main/java/com/baeldung/config/jackson/DomainModelModule.java b/spring-roo/src/main/java/com/baeldung/config/jackson/DomainModelModule.java new file mode 100644 index 000000000000..2a66a4884634 --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/config/jackson/DomainModelModule.java @@ -0,0 +1,11 @@ +package com.baeldung.config.jackson; +import org.springframework.roo.addon.web.mvc.controller.annotations.config.RooDomainModelModule; + +/** + * = DomainModelModule + TODO Auto-generated class documentation + * + */ +@RooDomainModelModule +public class DomainModelModule { +} diff --git a/spring-roo/src/main/java/com/baeldung/config/jackson/DomainModelModule_Roo_DomainModelModule.aj b/spring-roo/src/main/java/com/baeldung/config/jackson/DomainModelModule_Roo_DomainModelModule.aj new file mode 100644 index 000000000000..4683a3fe2d56 --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/config/jackson/DomainModelModule_Roo_DomainModelModule.aj @@ -0,0 +1,28 @@ +// WARNING: DO NOT EDIT THIS FILE. THIS FILE IS MANAGED BY SPRING ROO. +// You may push code into the target .java compilation unit if you wish to edit any member(s). + +package com.baeldung.config.jackson; + +import com.baeldung.config.jackson.DomainModelModule; +import com.baeldung.domain.Book; +import com.baeldung.web.BookJsonMixin; +import com.fasterxml.jackson.databind.module.SimpleModule; +import org.springframework.boot.jackson.JsonComponent; + +privileged aspect DomainModelModule_Roo_DomainModelModule { + + declare parents: DomainModelModule extends SimpleModule; + + declare @type: DomainModelModule: @JsonComponent; + + /** + * TODO Auto-generated constructor documentation + * + */ + public DomainModelModule.new() { + // Mixin registration + + setMixInAnnotation(Book.class, BookJsonMixin.class); + } + +} diff --git a/spring-roo/src/main/java/com/baeldung/domain/Book.java b/spring-roo/src/main/java/com/baeldung/domain/Book.java new file mode 100644 index 000000000000..99e7b894e6b2 --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/domain/Book.java @@ -0,0 +1,58 @@ +package com.baeldung.domain; +import org.springframework.roo.addon.javabean.annotations.RooEquals; +import org.springframework.roo.addon.javabean.annotations.RooJavaBean; +import org.springframework.roo.addon.javabean.annotations.RooToString; +import org.springframework.roo.addon.jpa.annotations.entity.RooJpaEntity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Version; +import javax.validation.constraints.NotNull; + +/** + * = Book + TODO Auto-generated class documentation + * + */ +@RooJavaBean +@RooToString +@RooJpaEntity +@RooEquals(isJpaEntity = true) +public class Book { + + /** + * TODO Auto-generated attribute documentation + * + */ + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + /** + * TODO Auto-generated attribute documentation + * + */ + @Version + private Integer version; + + /** + * TODO Auto-generated attribute documentation + * + */ + @NotNull + private String title; + + /** + * TODO Auto-generated attribute documentation + * + */ + @NotNull + private String author; + + /** + * TODO Auto-generated attribute documentation + * + */ + @NotNull + private String isbn; +} diff --git a/spring-roo/src/main/java/com/baeldung/domain/Book_Roo_Equals.aj b/spring-roo/src/main/java/com/baeldung/domain/Book_Roo_Equals.aj new file mode 100644 index 000000000000..51d6069eb04e --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/domain/Book_Roo_Equals.aj @@ -0,0 +1,41 @@ +// WARNING: DO NOT EDIT THIS FILE. THIS FILE IS MANAGED BY SPRING ROO. +// You may push code into the target .java compilation unit if you wish to edit any member(s). + +package com.baeldung.domain; + +import com.baeldung.domain.Book; +import java.util.Objects; + +privileged aspect Book_Roo_Equals { + + /** + * This `equals` implementation is specific for JPA entities and uses + * the entity identifier for it, following the article in + * https://vladmihalcea.com/2016/06/06/how-to-implement-equals-and-hashcode-using-the-jpa-entity-identifier/ + * + * @param obj + * @return Boolean + */ + public boolean Book.equals(Object obj) { + if (this == obj) { + return true; + } + // instanceof is false if the instance is null + if (!(obj instanceof Book)) { + return false; + } + return getId() != null && Objects.equals(getId(), ((Book) obj).getId()); + } + + /** + * This `hashCode` implementation is specific for JPA entities and uses a fixed `int` value to be able + * to identify the entity in collections after a new id is assigned to the entity, following the article in + * https://vladmihalcea.com/2016/06/06/how-to-implement-equals-and-hashcode-using-the-jpa-entity-identifier/ + * + * @return Integer + */ + public int Book.hashCode() { + return 31; + } + +} diff --git a/spring-roo/src/main/java/com/baeldung/domain/Book_Roo_JavaBean.aj b/spring-roo/src/main/java/com/baeldung/domain/Book_Roo_JavaBean.aj new file mode 100644 index 000000000000..3ea375ce1259 --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/domain/Book_Roo_JavaBean.aj @@ -0,0 +1,100 @@ +// WARNING: DO NOT EDIT THIS FILE. THIS FILE IS MANAGED BY SPRING ROO. +// You may push code into the target .java compilation unit if you wish to edit any member(s). + +package com.baeldung.domain; + +import com.baeldung.domain.Book; + +privileged aspect Book_Roo_JavaBean { + + /** + * TODO Auto-generated method documentation + * + * @return Long + */ + public Long Book.getId() { + return this.id; + } + + /** + * TODO Auto-generated method documentation + * + * @param id + */ + public void Book.setId(Long id) { + this.id = id; + } + + /** + * TODO Auto-generated method documentation + * + * @return Integer + */ + public Integer Book.getVersion() { + return this.version; + } + + /** + * TODO Auto-generated method documentation + * + * @param version + */ + public void Book.setVersion(Integer version) { + this.version = version; + } + + /** + * TODO Auto-generated method documentation + * + * @return String + */ + public String Book.getTitle() { + return this.title; + } + + /** + * TODO Auto-generated method documentation + * + * @param title + */ + public void Book.setTitle(String title) { + this.title = title; + } + + /** + * TODO Auto-generated method documentation + * + * @return String + */ + public String Book.getAuthor() { + return this.author; + } + + /** + * TODO Auto-generated method documentation + * + * @param author + */ + public void Book.setAuthor(String author) { + this.author = author; + } + + /** + * TODO Auto-generated method documentation + * + * @return String + */ + public String Book.getIsbn() { + return this.isbn; + } + + /** + * TODO Auto-generated method documentation + * + * @param isbn + */ + public void Book.setIsbn(String isbn) { + this.isbn = isbn; + } + +} diff --git a/spring-roo/src/main/java/com/baeldung/domain/Book_Roo_Jpa_Entity.aj b/spring-roo/src/main/java/com/baeldung/domain/Book_Roo_Jpa_Entity.aj new file mode 100644 index 000000000000..739a00e0584d --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/domain/Book_Roo_Jpa_Entity.aj @@ -0,0 +1,28 @@ +// WARNING: DO NOT EDIT THIS FILE. THIS FILE IS MANAGED BY SPRING ROO. +// You may push code into the target .java compilation unit if you wish to edit any member(s). + +package com.baeldung.domain; + +import com.baeldung.domain.Book; +import io.springlets.format.EntityFormat; +import javax.persistence.Entity; + +privileged aspect Book_Roo_Jpa_Entity { + + declare @type: Book: @Entity; + + declare @type: Book: @EntityFormat; + + /** + * TODO Auto-generated attribute documentation + * + */ + public static final String Book.ITERABLE_TO_ADD_CANT_BE_NULL_MESSAGE = "The given Iterable of items to add can't be null!"; + + /** + * TODO Auto-generated attribute documentation + * + */ + public static final String Book.ITERABLE_TO_REMOVE_CANT_BE_NULL_MESSAGE = "The given Iterable of items to add can't be null!"; + +} diff --git a/spring-roo/src/main/java/com/baeldung/domain/Book_Roo_ToString.aj b/spring-roo/src/main/java/com/baeldung/domain/Book_Roo_ToString.aj new file mode 100644 index 000000000000..69083eaf9eff --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/domain/Book_Roo_ToString.aj @@ -0,0 +1,26 @@ +// WARNING: DO NOT EDIT THIS FILE. THIS FILE IS MANAGED BY SPRING ROO. +// You may push code into the target .java compilation unit if you wish to edit any member(s). + +package com.baeldung.domain; + +import com.baeldung.domain.Book; + +privileged aspect Book_Roo_ToString { + + /** + * TODO Auto-generated method documentation + * + * @return String + */ + public String Book.toString() { + return "Book {" + + "id='" + id + '\'' + + ", version='" + version + '\'' + + ", title='" + title + '\'' + + ", author='" + author + '\'' + + ", isbn='" + isbn + '\'' + + ", ITERABLE_TO_ADD_CANT_BE_NULL_MESSAGE='" + ITERABLE_TO_ADD_CANT_BE_NULL_MESSAGE + '\'' + + ", ITERABLE_TO_REMOVE_CANT_BE_NULL_MESSAGE='" + ITERABLE_TO_REMOVE_CANT_BE_NULL_MESSAGE + '\'' + "}" + super.toString(); + } + +} diff --git a/spring-roo/src/main/java/com/baeldung/repository/BookRepository.java b/spring-roo/src/main/java/com/baeldung/repository/BookRepository.java new file mode 100644 index 000000000000..78567c1c924a --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/repository/BookRepository.java @@ -0,0 +1,12 @@ +package com.baeldung.repository; +import com.baeldung.domain.Book; +import org.springframework.roo.addon.layers.repository.jpa.annotations.RooJpaRepository; + +/** + * = BookRepository + TODO Auto-generated class documentation + * + */ +@RooJpaRepository(entity = Book.class) +public interface BookRepository { +} diff --git a/spring-roo/src/main/java/com/baeldung/repository/BookRepositoryCustom.java b/spring-roo/src/main/java/com/baeldung/repository/BookRepositoryCustom.java new file mode 100644 index 000000000000..44469e31b6e5 --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/repository/BookRepositoryCustom.java @@ -0,0 +1,12 @@ +package com.baeldung.repository; +import com.baeldung.domain.Book; +import org.springframework.roo.addon.layers.repository.jpa.annotations.RooJpaRepositoryCustom; + +/** + * = BookRepositoryCustom + TODO Auto-generated class documentation + * + */ +@RooJpaRepositoryCustom(entity = Book.class) +public interface BookRepositoryCustom { +} diff --git a/spring-roo/src/main/java/com/baeldung/repository/BookRepositoryCustom_Roo_Jpa_Repository_Custom.aj b/spring-roo/src/main/java/com/baeldung/repository/BookRepositoryCustom_Roo_Jpa_Repository_Custom.aj new file mode 100644 index 000000000000..7cb44f84a129 --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/repository/BookRepositoryCustom_Roo_Jpa_Repository_Custom.aj @@ -0,0 +1,23 @@ +// WARNING: DO NOT EDIT THIS FILE. THIS FILE IS MANAGED BY SPRING ROO. +// You may push code into the target .java compilation unit if you wish to edit any member(s). + +package com.baeldung.repository; + +import com.baeldung.domain.Book; +import com.baeldung.repository.BookRepositoryCustom; +import io.springlets.data.domain.GlobalSearch; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +privileged aspect BookRepositoryCustom_Roo_Jpa_Repository_Custom { + + /** + * TODO Auto-generated method documentation + * + * @param globalSearch + * @param pageable + * @return Page + */ + public abstract Page BookRepositoryCustom.findAll(GlobalSearch globalSearch, Pageable pageable); + +} diff --git a/spring-roo/src/main/java/com/baeldung/repository/BookRepositoryImpl.java b/spring-roo/src/main/java/com/baeldung/repository/BookRepositoryImpl.java new file mode 100644 index 000000000000..e59ff8ab0eb7 --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/repository/BookRepositoryImpl.java @@ -0,0 +1,22 @@ +package com.baeldung.repository; + +import io.springlets.data.jpa.repository.support.QueryDslRepositorySupportExt; +import org.springframework.roo.addon.layers.repository.jpa.annotations.RooJpaRepositoryCustomImpl; +import com.baeldung.domain.Book; + +/** + * = BookRepositoryImpl + * + * TODO Auto-generated class documentation + * + */ +@RooJpaRepositoryCustomImpl(repository = BookRepositoryCustom.class) +public class BookRepositoryImpl extends QueryDslRepositorySupportExt { + + /** + * TODO Auto-generated constructor documentation + */ + BookRepositoryImpl() { + super(Book.class); + } +} \ No newline at end of file diff --git a/spring-roo/src/main/java/com/baeldung/repository/BookRepositoryImpl_Roo_Jpa_Repository_Impl.aj b/spring-roo/src/main/java/com/baeldung/repository/BookRepositoryImpl_Roo_Jpa_Repository_Impl.aj new file mode 100644 index 000000000000..9e3db3e1f4d7 --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/repository/BookRepositoryImpl_Roo_Jpa_Repository_Impl.aj @@ -0,0 +1,69 @@ +// WARNING: DO NOT EDIT THIS FILE. THIS FILE IS MANAGED BY SPRING ROO. +// You may push code into the target .java compilation unit if you wish to edit any member(s). + +package com.baeldung.repository; + +import com.baeldung.domain.Book; +import com.baeldung.domain.QBook; +import com.baeldung.repository.BookRepositoryCustom; +import com.baeldung.repository.BookRepositoryImpl; +import com.querydsl.core.types.Path; +import com.querydsl.jpa.JPQLQuery; +import io.springlets.data.domain.GlobalSearch; +import io.springlets.data.jpa.repository.support.QueryDslRepositorySupportExt.AttributeMappingBuilder; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.transaction.annotation.Transactional; + +privileged aspect BookRepositoryImpl_Roo_Jpa_Repository_Impl { + + declare parents: BookRepositoryImpl implements BookRepositoryCustom; + + declare @type: BookRepositoryImpl: @Transactional(readOnly = true); + + /** + * TODO Auto-generated attribute documentation + * + */ + public static final String BookRepositoryImpl.TITLE = "title"; + + /** + * TODO Auto-generated attribute documentation + * + */ + public static final String BookRepositoryImpl.AUTHOR = "author"; + + /** + * TODO Auto-generated attribute documentation + * + */ + public static final String BookRepositoryImpl.ISBN = "isbn"; + + /** + * TODO Auto-generated method documentation + * + * @param globalSearch + * @param pageable + * @return Page + */ + public Page BookRepositoryImpl.findAll(GlobalSearch globalSearch, Pageable pageable) { + + QBook book = QBook.book; + + JPQLQuery query = from(book); + + Path[] paths = new Path[] {book.title,book.author,book.isbn}; + applyGlobalSearch(globalSearch, query, paths); + + AttributeMappingBuilder mapping = buildMapper() + .map(TITLE, book.title) + .map(AUTHOR, book.author) + .map(ISBN, book.isbn); + + applyPagination(pageable, query, mapping); + applyOrderById(query); + + return loadPage(query, pageable, book); + } + +} diff --git a/spring-roo/src/main/java/com/baeldung/repository/BookRepository_Roo_Jpa_Repository.aj b/spring-roo/src/main/java/com/baeldung/repository/BookRepository_Roo_Jpa_Repository.aj new file mode 100644 index 000000000000..2f570620544e --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/repository/BookRepository_Roo_Jpa_Repository.aj @@ -0,0 +1,20 @@ +// WARNING: DO NOT EDIT THIS FILE. THIS FILE IS MANAGED BY SPRING ROO. +// You may push code into the target .java compilation unit if you wish to edit any member(s). + +package com.baeldung.repository; + +import com.baeldung.domain.Book; +import com.baeldung.repository.BookRepository; +import com.baeldung.repository.BookRepositoryCustom; +import io.springlets.data.jpa.repository.DetachableJpaRepository; +import org.springframework.transaction.annotation.Transactional; + +privileged aspect BookRepository_Roo_Jpa_Repository { + + declare parents: BookRepository extends DetachableJpaRepository; + + declare parents: BookRepository extends BookRepositoryCustom; + + declare @type: BookRepository: @Transactional(readOnly = true); + +} diff --git a/spring-roo/src/main/java/com/baeldung/service/api/BookService.java b/spring-roo/src/main/java/com/baeldung/service/api/BookService.java new file mode 100644 index 000000000000..212ee1cdfce9 --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/service/api/BookService.java @@ -0,0 +1,12 @@ +package com.baeldung.service.api; +import com.baeldung.domain.Book; +import org.springframework.roo.addon.layers.service.annotations.RooService; + +/** + * = BookService + TODO Auto-generated class documentation + * + */ +@RooService(entity = Book.class) +public interface BookService { +} diff --git a/spring-roo/src/main/java/com/baeldung/service/api/BookService_Roo_Service.aj b/spring-roo/src/main/java/com/baeldung/service/api/BookService_Roo_Service.aj new file mode 100644 index 000000000000..d5698ffb37c4 --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/service/api/BookService_Roo_Service.aj @@ -0,0 +1,95 @@ +// WARNING: DO NOT EDIT THIS FILE. THIS FILE IS MANAGED BY SPRING ROO. +// You may push code into the target .java compilation unit if you wish to edit any member(s). + +package com.baeldung.service.api; + +import com.baeldung.domain.Book; +import com.baeldung.service.api.BookService; +import io.springlets.data.domain.GlobalSearch; +import io.springlets.format.EntityResolver; +import java.util.List; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +privileged aspect BookService_Roo_Service { + + declare parents: BookService extends EntityResolver; + + /** + * TODO Auto-generated method documentation + * + * @param id + * @return Book + */ + public abstract Book BookService.findOne(Long id); + + /** + * TODO Auto-generated method documentation + * + * @param book + */ + public abstract void BookService.delete(Book book); + + /** + * TODO Auto-generated method documentation + * + * @param entities + * @return List + */ + public abstract List BookService.save(Iterable entities); + + /** + * TODO Auto-generated method documentation + * + * @param ids + */ + public abstract void BookService.delete(Iterable ids); + + /** + * TODO Auto-generated method documentation + * + * @param entity + * @return Book + */ + public abstract Book BookService.save(Book entity); + + /** + * TODO Auto-generated method documentation + * + * @param id + * @return Book + */ + public abstract Book BookService.findOneForUpdate(Long id); + + /** + * TODO Auto-generated method documentation + * + * @param ids + * @return List + */ + public abstract List BookService.findAll(Iterable ids); + + /** + * TODO Auto-generated method documentation + * + * @return List + */ + public abstract List BookService.findAll(); + + /** + * TODO Auto-generated method documentation + * + * @return Long + */ + public abstract long BookService.count(); + + /** + * TODO Auto-generated method documentation + * + * @param globalSearch + * @param pageable + * @return Page + */ + public abstract Page BookService.findAll(GlobalSearch globalSearch, Pageable pageable); + +} diff --git a/spring-roo/src/main/java/com/baeldung/service/impl/BookServiceImpl.java b/spring-roo/src/main/java/com/baeldung/service/impl/BookServiceImpl.java new file mode 100644 index 000000000000..1b445476298e --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/service/impl/BookServiceImpl.java @@ -0,0 +1,12 @@ +package com.baeldung.service.impl; +import com.baeldung.service.api.BookService; +import org.springframework.roo.addon.layers.service.annotations.RooServiceImpl; + +/** + * = BookServiceImpl + TODO Auto-generated class documentation + * + */ +@RooServiceImpl(service = BookService.class) +public class BookServiceImpl implements BookService { +} diff --git a/spring-roo/src/main/java/com/baeldung/service/impl/BookServiceImpl_Roo_Service_Impl.aj b/spring-roo/src/main/java/com/baeldung/service/impl/BookServiceImpl_Roo_Service_Impl.aj new file mode 100644 index 000000000000..8f9fb846aa55 --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/service/impl/BookServiceImpl_Roo_Service_Impl.aj @@ -0,0 +1,177 @@ +// WARNING: DO NOT EDIT THIS FILE. THIS FILE IS MANAGED BY SPRING ROO. +// You may push code into the target .java compilation unit if you wish to edit any member(s). + +package com.baeldung.service.impl; + +import com.baeldung.domain.Book; +import com.baeldung.repository.BookRepository; +import com.baeldung.service.impl.BookServiceImpl; +import io.springlets.data.domain.GlobalSearch; +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +privileged aspect BookServiceImpl_Roo_Service_Impl { + + declare @type: BookServiceImpl: @Service; + + declare @type: BookServiceImpl: @Transactional(readOnly = true); + + /** + * TODO Auto-generated attribute documentation + * + */ + private BookRepository BookServiceImpl.bookRepository; + + /** + * TODO Auto-generated constructor documentation + * + * @param bookRepository + */ + @Autowired + public BookServiceImpl.new(BookRepository bookRepository) { + setBookRepository(bookRepository); + } + + /** + * TODO Auto-generated method documentation + * + * @return BookRepository + */ + public BookRepository BookServiceImpl.getBookRepository() { + return bookRepository; + } + + /** + * TODO Auto-generated method documentation + * + * @param bookRepository + */ + public void BookServiceImpl.setBookRepository(BookRepository bookRepository) { + this.bookRepository = bookRepository; + } + + /** + * TODO Auto-generated method documentation + * + * @param book + */ + @Transactional + public void BookServiceImpl.delete(Book book) { + getBookRepository().delete(book); + } + + /** + * TODO Auto-generated method documentation + * + * @param entities + * @return List + */ + @Transactional + public List BookServiceImpl.save(Iterable entities) { + return getBookRepository().save(entities); + } + + /** + * TODO Auto-generated method documentation + * + * @param ids + */ + @Transactional + public void BookServiceImpl.delete(Iterable ids) { + List toDelete = getBookRepository().findAll(ids); + getBookRepository().deleteInBatch(toDelete); + } + + /** + * TODO Auto-generated method documentation + * + * @param entity + * @return Book + */ + @Transactional + public Book BookServiceImpl.save(Book entity) { + return getBookRepository().save(entity); + } + + /** + * TODO Auto-generated method documentation + * + * @param id + * @return Book + */ + public Book BookServiceImpl.findOne(Long id) { + return getBookRepository().findOne(id); + } + + /** + * TODO Auto-generated method documentation + * + * @param id + * @return Book + */ + public Book BookServiceImpl.findOneForUpdate(Long id) { + return getBookRepository().findOneDetached(id); + } + + /** + * TODO Auto-generated method documentation + * + * @param ids + * @return List + */ + public List BookServiceImpl.findAll(Iterable ids) { + return getBookRepository().findAll(ids); + } + + /** + * TODO Auto-generated method documentation + * + * @return List + */ + public List BookServiceImpl.findAll() { + return getBookRepository().findAll(); + } + + /** + * TODO Auto-generated method documentation + * + * @return Long + */ + public long BookServiceImpl.count() { + return getBookRepository().count(); + } + + /** + * TODO Auto-generated method documentation + * + * @param globalSearch + * @param pageable + * @return Page + */ + public Page BookServiceImpl.findAll(GlobalSearch globalSearch, Pageable pageable) { + return getBookRepository().findAll(globalSearch, pageable); + } + + /** + * TODO Auto-generated method documentation + * + * @return Class + */ + public Class BookServiceImpl.getEntityType() { + return Book.class; + } + + /** + * TODO Auto-generated method documentation + * + * @return Class + */ + public Class BookServiceImpl.getIdType() { + return Long.class; + } + +} diff --git a/spring-roo/src/main/java/com/baeldung/web/BookDeserializer.java b/spring-roo/src/main/java/com/baeldung/web/BookDeserializer.java new file mode 100644 index 000000000000..38c447e580dc --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/web/BookDeserializer.java @@ -0,0 +1,41 @@ +package com.baeldung.web; +import com.baeldung.domain.Book; +import com.baeldung.service.api.BookService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.jackson.JsonObjectDeserializer; +import org.springframework.context.annotation.Lazy; +import org.springframework.core.convert.ConversionService; +import org.springframework.roo.addon.web.mvc.controller.annotations.config.RooDeserializer; + +/** + * = BookDeserializer + TODO Auto-generated class documentation + * + */ +@RooDeserializer(entity = Book.class) +public class BookDeserializer extends JsonObjectDeserializer { + + /** + * TODO Auto-generated attribute documentation + * + */ + private BookService bookService; + + /** + * TODO Auto-generated attribute documentation + * + */ + private ConversionService conversionService; + + /** + * TODO Auto-generated constructor documentation + * + * @param bookService + * @param conversionService + */ + @Autowired + public BookDeserializer(@Lazy BookService bookService, ConversionService conversionService) { + this.bookService = bookService; + this.conversionService = conversionService; + } +} diff --git a/spring-roo/src/main/java/com/baeldung/web/BookDeserializer_Roo_EntityDeserializer.aj b/spring-roo/src/main/java/com/baeldung/web/BookDeserializer_Roo_EntityDeserializer.aj new file mode 100644 index 000000000000..5ca42cfb5edb --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/web/BookDeserializer_Roo_EntityDeserializer.aj @@ -0,0 +1,78 @@ +// WARNING: DO NOT EDIT THIS FILE. THIS FILE IS MANAGED BY SPRING ROO. +// You may push code into the target .java compilation unit if you wish to edit any member(s). + +package com.baeldung.web; + +import com.baeldung.domain.Book; +import com.baeldung.service.api.BookService; +import com.baeldung.web.BookDeserializer; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.ObjectCodec; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import io.springlets.web.NotFoundException; +import java.io.IOException; +import org.springframework.boot.jackson.JsonComponent; +import org.springframework.core.convert.ConversionService; + +privileged aspect BookDeserializer_Roo_EntityDeserializer { + + declare @type: BookDeserializer: @JsonComponent; + + /** + * TODO Auto-generated method documentation + * + * @return BookService + */ + public BookService BookDeserializer.getBookService() { + return bookService; + } + + /** + * TODO Auto-generated method documentation + * + * @param bookService + */ + public void BookDeserializer.setBookService(BookService bookService) { + this.bookService = bookService; + } + + /** + * TODO Auto-generated method documentation + * + * @return ConversionService + */ + public ConversionService BookDeserializer.getConversionService() { + return conversionService; + } + + /** + * TODO Auto-generated method documentation + * + * @param conversionService + */ + public void BookDeserializer.setConversionService(ConversionService conversionService) { + this.conversionService = conversionService; + } + + /** + * TODO Auto-generated method documentation + * + * @param jsonParser + * @param context + * @param codec + * @param tree + * @return Book + * @throws IOException + */ + public Book BookDeserializer.deserializeObject(JsonParser jsonParser, DeserializationContext context, ObjectCodec codec, JsonNode tree) throws IOException { + String idText = tree.asText(); + Long id = conversionService.convert(idText, Long.class); + Book book = bookService.findOne(id); + if (book == null) { + throw new NotFoundException("Book not found"); + } + return book; + } + +} diff --git a/spring-roo/src/main/java/com/baeldung/web/BookJsonMixin.java b/spring-roo/src/main/java/com/baeldung/web/BookJsonMixin.java new file mode 100644 index 000000000000..1cde8f0e86f7 --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/web/BookJsonMixin.java @@ -0,0 +1,12 @@ +package com.baeldung.web; +import com.baeldung.domain.Book; +import org.springframework.roo.addon.web.mvc.controller.annotations.config.RooJsonMixin; + +/** + * = BookJsonMixin + TODO Auto-generated class documentation + * + */ +@RooJsonMixin(entity = Book.class) +public abstract class BookJsonMixin { +} diff --git a/spring-roo/src/main/java/com/baeldung/web/BooksCollectionThymeleafController.java b/spring-roo/src/main/java/com/baeldung/web/BooksCollectionThymeleafController.java new file mode 100644 index 000000000000..9b0179e234fd --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/web/BooksCollectionThymeleafController.java @@ -0,0 +1,15 @@ +package com.baeldung.web; +import com.baeldung.domain.Book; +import org.springframework.roo.addon.web.mvc.controller.annotations.ControllerType; +import org.springframework.roo.addon.web.mvc.controller.annotations.RooController; +import org.springframework.roo.addon.web.mvc.thymeleaf.annotations.RooThymeleaf; + +/** + * = BooksCollectionThymeleafController + TODO Auto-generated class documentation + * + */ +@RooController(entity = Book.class, type = ControllerType.COLLECTION) +@RooThymeleaf +public class BooksCollectionThymeleafController { +} diff --git a/spring-roo/src/main/java/com/baeldung/web/BooksCollectionThymeleafController_Roo_Controller.aj b/spring-roo/src/main/java/com/baeldung/web/BooksCollectionThymeleafController_Roo_Controller.aj new file mode 100644 index 000000000000..a87b76a74537 --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/web/BooksCollectionThymeleafController_Roo_Controller.aj @@ -0,0 +1,35 @@ +// WARNING: DO NOT EDIT THIS FILE. THIS FILE IS MANAGED BY SPRING ROO. +// You may push code into the target .java compilation unit if you wish to edit any member(s). + +package com.baeldung.web; + +import com.baeldung.service.api.BookService; +import com.baeldung.web.BooksCollectionThymeleafController; + +privileged aspect BooksCollectionThymeleafController_Roo_Controller { + + /** + * TODO Auto-generated attribute documentation + * + */ + private BookService BooksCollectionThymeleafController.bookService; + + /** + * TODO Auto-generated method documentation + * + * @return BookService + */ + public BookService BooksCollectionThymeleafController.getBookService() { + return bookService; + } + + /** + * TODO Auto-generated method documentation + * + * @param bookService + */ + public void BooksCollectionThymeleafController.setBookService(BookService bookService) { + this.bookService = bookService; + } + +} diff --git a/spring-roo/src/main/java/com/baeldung/web/BooksCollectionThymeleafController_Roo_Thymeleaf.aj b/spring-roo/src/main/java/com/baeldung/web/BooksCollectionThymeleafController_Roo_Thymeleaf.aj new file mode 100644 index 000000000000..b0831730bef7 --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/web/BooksCollectionThymeleafController_Roo_Thymeleaf.aj @@ -0,0 +1,470 @@ +// WARNING: DO NOT EDIT THIS FILE. THIS FILE IS MANAGED BY SPRING ROO. +// You may push code into the target .java compilation unit if you wish to edit any member(s). + +package com.baeldung.web; + +import ar.com.fdvs.dj.core.DynamicJasperHelper; +import ar.com.fdvs.dj.core.layout.ClassicLayoutManager; +import ar.com.fdvs.dj.domain.builders.ColumnBuilderException; +import ar.com.fdvs.dj.domain.builders.FastReportBuilder; +import com.baeldung.domain.Book; +import com.baeldung.service.api.BookService; +import com.baeldung.web.BooksCollectionThymeleafController; +import com.baeldung.web.BooksItemThymeleafController; +import com.baeldung.web.BooksItemThymeleafLinkFactory; +import com.baeldung.web.reports.ExportingErrorException; +import com.baeldung.web.reports.JasperReportsCsvExporter; +import com.baeldung.web.reports.JasperReportsExporter; +import com.baeldung.web.reports.JasperReportsPdfExporter; +import com.baeldung.web.reports.JasperReportsXlsExporter; +import io.springlets.data.domain.GlobalSearch; +import io.springlets.data.web.datatables.ConvertedDatatablesData; +import io.springlets.data.web.datatables.Datatables; +import io.springlets.data.web.datatables.DatatablesColumns; +import io.springlets.data.web.datatables.DatatablesPageable; +import io.springlets.data.web.select2.Select2DataSupport; +import io.springlets.data.web.select2.Select2DataWithConversion; +import io.springlets.web.mvc.util.ControllerMethodLinkBuilderFactory; +import io.springlets.web.mvc.util.MethodLinkBuilderFactory; +import java.io.IOException; +import java.util.Collection; +import java.util.Locale; +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; +import net.sf.jasperreports.engine.JRDataSource; +import net.sf.jasperreports.engine.JRException; +import net.sf.jasperreports.engine.JasperPrint; +import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.core.convert.ConversionService; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.InitBinder; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.util.UriComponents; + +privileged aspect BooksCollectionThymeleafController_Roo_Thymeleaf { + + declare @type: BooksCollectionThymeleafController: @Controller; + + declare @type: BooksCollectionThymeleafController: @RequestMapping(value = "/books", name = "BooksCollectionThymeleafController", produces = MediaType.TEXT_HTML_VALUE); + + /** + * TODO Auto-generated attribute documentation + * + */ + private MessageSource BooksCollectionThymeleafController.messageSource; + + /** + * TODO Auto-generated attribute documentation + * + */ + private MethodLinkBuilderFactory BooksCollectionThymeleafController.itemLink; + + /** + * TODO Auto-generated attribute documentation + * + */ + private ConversionService BooksCollectionThymeleafController.conversionService; + + /** + * TODO Auto-generated constructor documentation + * + * @param bookService + * @param conversionService + * @param messageSource + * @param linkBuilder + */ + @Autowired + public BooksCollectionThymeleafController.new(BookService bookService, ConversionService conversionService, MessageSource messageSource, ControllerMethodLinkBuilderFactory linkBuilder) { + setBookService(bookService); + setConversionService(conversionService); + setMessageSource(messageSource); + setItemLink(linkBuilder.of(BooksItemThymeleafController.class)); + } + + /** + * TODO Auto-generated method documentation + * + * @return MessageSource + */ + public MessageSource BooksCollectionThymeleafController.getMessageSource() { + return messageSource; + } + + /** + * TODO Auto-generated method documentation + * + * @param messageSource + */ + public void BooksCollectionThymeleafController.setMessageSource(MessageSource messageSource) { + this.messageSource = messageSource; + } + + /** + * TODO Auto-generated method documentation + * + * @return MethodLinkBuilderFactory + */ + public MethodLinkBuilderFactory BooksCollectionThymeleafController.getItemLink() { + return itemLink; + } + + /** + * TODO Auto-generated method documentation + * + * @param itemLink + */ + public void BooksCollectionThymeleafController.setItemLink(MethodLinkBuilderFactory itemLink) { + this.itemLink = itemLink; + } + + /** + * TODO Auto-generated method documentation + * + * @return ConversionService + */ + public ConversionService BooksCollectionThymeleafController.getConversionService() { + return conversionService; + } + + /** + * TODO Auto-generated method documentation + * + * @param conversionService + */ + public void BooksCollectionThymeleafController.setConversionService(ConversionService conversionService) { + this.conversionService = conversionService; + } + + /** + * TODO Auto-generated method documentation + * + * @param model + * @return ModelAndView + */ + @GetMapping(name = "list") + public ModelAndView BooksCollectionThymeleafController.list(Model model) { + return new ModelAndView("/books/list"); + } + + /** + * TODO Auto-generated method documentation + * + * @param datatablesColumns + * @param search + * @param pageable + * @param draw + * @return ResponseEntity + */ + @GetMapping(produces = Datatables.MEDIA_TYPE, name = "datatables", value = "/dt") + @ResponseBody + public ResponseEntity> BooksCollectionThymeleafController.datatables(DatatablesColumns datatablesColumns, GlobalSearch search, DatatablesPageable pageable, @RequestParam("draw") Integer draw) { + Page books = getBookService().findAll(search, pageable); + long totalBooksCount = books.getTotalElements(); + if (search != null && StringUtils.isNotBlank(search.getText())) { + totalBooksCount = getBookService().count(); + } + ConvertedDatatablesData datatablesData = new ConvertedDatatablesData(books, totalBooksCount, draw, getConversionService(), datatablesColumns); + return ResponseEntity.ok(datatablesData); + } + + /** + * TODO Auto-generated method documentation + * + * @param search + * @param pageable + * @param locale + * @return ResponseEntity + */ + @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE, name = "select2", value = "/s2") + @ResponseBody + public ResponseEntity> BooksCollectionThymeleafController.select2(GlobalSearch search, Pageable pageable, Locale locale) { + Page books = getBookService().findAll(search, pageable); + String idExpression = "#{id}"; + Select2DataSupport select2Data = new Select2DataWithConversion(books, idExpression, getConversionService()); + return ResponseEntity.ok(select2Data); + } + + /** + * TODO Auto-generated method documentation + * + * @param dataBinder + */ + @InitBinder("book") + public void BooksCollectionThymeleafController.initBookBinder(WebDataBinder dataBinder) { + dataBinder.setDisallowedFields("id"); + } + + /** + * TODO Auto-generated method documentation + * + * @param model + */ + public void BooksCollectionThymeleafController.populateFormats(Model model) { + model.addAttribute("application_locale", LocaleContextHolder.getLocale().getLanguage()); + } + + /** + * TODO Auto-generated method documentation + * + * @param model + */ + public void BooksCollectionThymeleafController.populateForm(Model model) { + populateFormats(model); + } + + /** + * TODO Auto-generated method documentation + * + * @param book + * @param result + * @param model + * @return ModelAndView + */ + @PostMapping(name = "create") + public ModelAndView BooksCollectionThymeleafController.create(@Valid @ModelAttribute Book book, BindingResult result, Model model) { + if (result.hasErrors()) { + populateForm(model); + + return new ModelAndView("/books/create"); + } + Book newBook = getBookService().save(book); + UriComponents showURI = getItemLink().to(BooksItemThymeleafLinkFactory.SHOW).with("book", newBook.getId()).toUri(); + return new ModelAndView("redirect:" + showURI.toUriString()); + } + + /** + * TODO Auto-generated method documentation + * + * @param model + * @return ModelAndView + */ + @GetMapping(value = "/create-form", name = "createForm") + public ModelAndView BooksCollectionThymeleafController.createForm(Model model) { + populateForm(model); + + model.addAttribute("book", new Book()); + return new ModelAndView("books/create"); + } + + /** + * TODO Auto-generated method documentation + * + * @param ids + * @return ResponseEntity + */ + @DeleteMapping(value = "/batch/{ids}", name = "deleteBatch") + @ResponseBody + public ResponseEntity BooksCollectionThymeleafController.deleteBatch(@PathVariable("ids") Collection ids) { + + getBookService().delete(ids); + + return ResponseEntity.ok().build(); + } + + /** + * Method that obtains the filtered and ordered records using the Datatables information and + * export them to a new report file. (It ignores the current pagination). + * + * To generate the report file it uses the `DynamicJasper` library + * (http://dynamicjasper.com). This library allows developers to generate reports dynamically + * without use an specific template to each entity. + * + * To customize the appearance of ALL generated reports, you could customize the + * "export_default.jrxml" template located in "src/main/resources/templates/reports/". However, + * if you want to customize the appearance of this specific report, you could create a new + * ".jrxml" file and provide it to the library replacing the `builder.setTemplateFile();` + * operation used in this implementation. + * + * @param search GlobalSearch that contains the filter provided by the Datatables component. + * @param pageable Pageable that contains the Sort info provided by the Datatabes component. + * @param datatablesColumns Columns displayed in the Datatables component. + * @param response The HttpServletResponse. + * @param exporter An specific JasperReportsExporter to be used during export process. + * @param fileName The final filename to use. + * @param locale The current Locale in the view context. + */ + public void BooksCollectionThymeleafController.export(GlobalSearch search, @PageableDefault(size = 2147483647) Pageable pageable, String[] datatablesColumns, HttpServletResponse response, JasperReportsExporter exporter, String fileName, Locale locale) { + // Obtain the filtered and ordered elements + Page books = getBookService().findAll(search, pageable); + + // Prevent generation of reports with empty data + if (books == null || books.getContent().isEmpty()) { + return; + } + + // Creates a new ReportBuilder using DynamicJasper library + FastReportBuilder builder = new FastReportBuilder(); + + // IMPORTANT: By default, this application uses "export_default.jrxml" + // to generate all reports. If you want to customize this specific report, + // create a new ".jrxml" template and customize it. (Take in account the + // DynamicJasper restrictions: + // http://dynamicjasper.com/2010/10/06/how-to-use-custom-jrxml-templates/) + builder.setTemplateFile("templates/reports/export_default.jrxml"); + + // The generated report will display the same columns as the Datatables component. + // However, this is not mandatory. You could edit this code if you want to ignore + // the provided datatablesColumns + if (datatablesColumns != null) { + for (String column : datatablesColumns) { + // Delegates in addColumnToReportBuilder to include each datatables column + // to the report builder + addColumnToReportBuilder(column, builder, locale, fileName); + } + } + + // This property resizes the columns to use full width page. + // Set false value if you want to use the specific width of each column. + builder.setUseFullPageWidth(true); + + // Creates a new Jasper Reports Datasource using the obtained elements + JRDataSource ds = new JRBeanCollectionDataSource(books.getContent()); + + // Generates the JasperReport + JasperPrint jp; + try { + jp = DynamicJasperHelper.generateJasperPrint(builder.build(), new ClassicLayoutManager(), ds); + } + catch (JRException e) { + String errorMessage = getMessageSource().getMessage("error_exportingErrorException", + new Object[] {StringUtils.substringAfterLast(fileName, ".").toUpperCase()}, + String.format("Error while exporting data to StringUtils file", StringUtils. + substringAfterLast(fileName, ".").toUpperCase()), locale); + throw new ExportingErrorException(errorMessage); + } + + // Converts the JaspertReport element to a ByteArrayOutputStream and + // write it into the response stream using the provided JasperReportExporter + try { + exporter.export(jp, fileName, response); + } + catch (JRException e) { + String errorMessage = getMessageSource().getMessage("error_exportingErrorException", + new Object[] {StringUtils.substringAfterLast(fileName, ".").toUpperCase()}, + String.format("Error while exporting data to StringUtils file", StringUtils. + substringAfterLast(fileName, ".").toUpperCase()), locale); + throw new ExportingErrorException(errorMessage); + } + catch (IOException e) { + String errorMessage = getMessageSource().getMessage("error_exportingErrorException", + new Object[] {StringUtils.substringAfterLast(fileName, ".").toUpperCase()}, + String.format("Error while exporting data to StringUtils file", StringUtils. + substringAfterLast(fileName, ".").toUpperCase()), locale); + throw new ExportingErrorException(errorMessage); + } + } + + /** + * It delegates in the `export` method providing the necessary information + * to generate a CSV report. + * + * @param search The GlobalSearch that contains the filter provided by the Datatables component + * @param pageable The Pageable that contains the Sort info provided by the Datatabes component + * @param datatablesColumns The Columns displayed in the Datatables component + * @param response The HttpServletResponse + * @return ResponseEntity + */ + @GetMapping(name = "exportCsv", value = "/export/csv") + @ResponseBody + public ResponseEntity BooksCollectionThymeleafController.exportCsv(GlobalSearch search, @PageableDefault(size = 2147483647) Pageable pageable, @RequestParam("datatablesColumns") String[] datatablesColumns, HttpServletResponse response, Locale locale) { + export(search, pageable, datatablesColumns, response, new JasperReportsCsvExporter(), "books_report.csv", locale); + return ResponseEntity.ok().build(); + } + + /** + * It delegates in the `export` method providing the necessary information + * to generate a PDF report. + * + * @param search The GlobalSearch that contains the filter provided by the Datatables component + * @param pageable The Pageable that contains the Sort info provided by the Datatabes component + * @param datatablesColumns The Columns displayed in the Datatables component + * @param response The HttpServletResponse + * @return ResponseEntity + */ + @GetMapping(name = "exportPdf", value = "/export/pdf") + @ResponseBody + public ResponseEntity BooksCollectionThymeleafController.exportPdf(GlobalSearch search, @PageableDefault(size = 2147483647) Pageable pageable, @RequestParam("datatablesColumns") String[] datatablesColumns, HttpServletResponse response, Locale locale) { + export(search, pageable, datatablesColumns, response, new JasperReportsPdfExporter(), "books_report.pdf", locale); + return ResponseEntity.ok().build(); + } + + /** + * It delegates in the `export` method providing the necessary information + * to generate a XLS report. + * + * @param search The GlobalSearch that contains the filter provided by the Datatables component + * @param pageable The Pageable that contains the Sort info provided by the Datatabes component + * @param datatablesColumns The Columns displayed in the Datatables component + * @param response The HttpServletResponse + * @return ResponseEntity + */ + @GetMapping(name = "exportXls", value = "/export/xls") + @ResponseBody + public ResponseEntity BooksCollectionThymeleafController.exportXls(GlobalSearch search, @PageableDefault(size = 2147483647) Pageable pageable, @RequestParam("datatablesColumns") String[] datatablesColumns, HttpServletResponse response, Locale locale) { + export(search, pageable, datatablesColumns, response, new JasperReportsXlsExporter(), "books_report.xls", locale); + return ResponseEntity.ok().build(); + } + + /** + * This method contains all the entity fields that are able to be displayed in a + * report. The developer could add a new column to the report builder providing the + * field name and the builder where the new field will be added as column. + * + * @param columnName the field name to show as column + * @param builder The builder where the new field will be added as column. + */ + public void BooksCollectionThymeleafController.addColumnToReportBuilder(String columnName, FastReportBuilder builder, Locale locale, String fileName) { + try { + if (columnName.equals("id")) { + builder.addColumn(getMessageSource().getMessage("label_book_id", null, "Id", locale), "id", Long.class.getName(), 50); + } + else if (columnName.equals("version")) { + builder.addColumn(getMessageSource().getMessage("label_book_version", null, "Version", locale), "version", Integer.class.getName(), 100); + } + else if (columnName.equals("title")) { + builder.addColumn(getMessageSource().getMessage("label_book_title", null, "Title", locale), "title", String.class.getName(), 100); + } + else if (columnName.equals("author")) { + builder.addColumn(getMessageSource().getMessage("label_book_author", null, "Author", locale), "author", String.class.getName(), 100); + } + else if (columnName.equals("isbn")) { + builder.addColumn(getMessageSource().getMessage("label_book_isbn", null, "Isbn", locale), "isbn", String.class.getName(), 100); + } + } + catch (ColumnBuilderException e) { + String errorMessage = getMessageSource().getMessage("error_exportingErrorException", + new Object[] {StringUtils.substringAfterLast(fileName, ".").toUpperCase()}, + String.format("Error while exporting data to StringUtils file", StringUtils. + substringAfterLast(fileName, ".").toUpperCase()), locale); + throw new ExportingErrorException(errorMessage); + } + catch (ClassNotFoundException e) { + String errorMessage = getMessageSource().getMessage("error_exportingErrorException", + new Object[] {StringUtils.substringAfterLast(fileName, ".").toUpperCase()}, + String.format("Error while exporting data to StringUtils file", StringUtils. + substringAfterLast(fileName, ".").toUpperCase()), locale); + throw new ExportingErrorException(errorMessage); + } + } + +} diff --git a/spring-roo/src/main/java/com/baeldung/web/BooksCollectionThymeleafLinkFactory.java b/spring-roo/src/main/java/com/baeldung/web/BooksCollectionThymeleafLinkFactory.java new file mode 100644 index 000000000000..413a5e64a0ff --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/web/BooksCollectionThymeleafLinkFactory.java @@ -0,0 +1,11 @@ +package com.baeldung.web; +import org.springframework.roo.addon.web.mvc.thymeleaf.annotations.RooLinkFactory; + +/** + * = BooksCollectionThymeleafLinkFactory + TODO Auto-generated class documentation + * + */ +@RooLinkFactory(controller = BooksCollectionThymeleafController.class) +public class BooksCollectionThymeleafLinkFactory { +} diff --git a/spring-roo/src/main/java/com/baeldung/web/BooksCollectionThymeleafLinkFactory_Roo_LinkFactory.aj b/spring-roo/src/main/java/com/baeldung/web/BooksCollectionThymeleafLinkFactory_Roo_LinkFactory.aj new file mode 100644 index 000000000000..e6df3efcb75f --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/web/BooksCollectionThymeleafLinkFactory_Roo_LinkFactory.aj @@ -0,0 +1,122 @@ +// WARNING: DO NOT EDIT THIS FILE. THIS FILE IS MANAGED BY SPRING ROO. +// You may push code into the target .java compilation unit if you wish to edit any member(s). + +package com.baeldung.web; + +import com.baeldung.web.BooksCollectionThymeleafController; +import com.baeldung.web.BooksCollectionThymeleafLinkFactory; +import io.springlets.web.mvc.util.MethodLinkFactory; +import io.springlets.web.mvc.util.SpringletsMvcUriComponentsBuilder; +import java.util.Map; +import org.springframework.stereotype.Component; +import org.springframework.web.util.UriComponents; + +privileged aspect BooksCollectionThymeleafLinkFactory_Roo_LinkFactory { + + declare parents: BooksCollectionThymeleafLinkFactory implements MethodLinkFactory; + + declare @type: BooksCollectionThymeleafLinkFactory: @Component; + + /** + * TODO Auto-generated attribute documentation + * + */ + public static final String BooksCollectionThymeleafLinkFactory.LIST = "list"; + + /** + * TODO Auto-generated attribute documentation + * + */ + public static final String BooksCollectionThymeleafLinkFactory.DATATABLES = "datatables"; + + /** + * TODO Auto-generated attribute documentation + * + */ + public static final String BooksCollectionThymeleafLinkFactory.SELECT2 = "select2"; + + /** + * TODO Auto-generated attribute documentation + * + */ + public static final String BooksCollectionThymeleafLinkFactory.CREATE = "create"; + + /** + * TODO Auto-generated attribute documentation + * + */ + public static final String BooksCollectionThymeleafLinkFactory.CREATEFORM = "createForm"; + + /** + * TODO Auto-generated attribute documentation + * + */ + public static final String BooksCollectionThymeleafLinkFactory.DELETEBATCH = "deleteBatch"; + + /** + * TODO Auto-generated attribute documentation + * + */ + public static final String BooksCollectionThymeleafLinkFactory.EXPORTCSV = "exportCsv"; + + /** + * TODO Auto-generated attribute documentation + * + */ + public static final String BooksCollectionThymeleafLinkFactory.EXPORTPDF = "exportPdf"; + + /** + * TODO Auto-generated attribute documentation + * + */ + public static final String BooksCollectionThymeleafLinkFactory.EXPORTXLS = "exportXls"; + + /** + * TODO Auto-generated method documentation + * + * @return Class + */ + public Class BooksCollectionThymeleafLinkFactory.getControllerClass() { + return BooksCollectionThymeleafController.class; + } + + /** + * TODO Auto-generated method documentation + * + * @param methodName + * @param parameters + * @param pathVariables + * @return UriComponents + */ + public UriComponents BooksCollectionThymeleafLinkFactory.toUri(String methodName, Object[] parameters, Map pathVariables) { + if (methodName.equals(LIST)) { + return SpringletsMvcUriComponentsBuilder.fromMethodCall(SpringletsMvcUriComponentsBuilder.on(getControllerClass()).list(null)).buildAndExpand(pathVariables); + } + if (methodName.equals(DATATABLES)) { + return SpringletsMvcUriComponentsBuilder.fromMethodCall(SpringletsMvcUriComponentsBuilder.on(getControllerClass()).datatables(null, null, null, null)).buildAndExpand(pathVariables); + } + if (methodName.equals(SELECT2)) { + return SpringletsMvcUriComponentsBuilder.fromMethodCall(SpringletsMvcUriComponentsBuilder.on(getControllerClass()).select2(null, null, null)).buildAndExpand(pathVariables); + } + if (methodName.equals(CREATE)) { + return SpringletsMvcUriComponentsBuilder.fromMethodCall(SpringletsMvcUriComponentsBuilder.on(getControllerClass()).create(null, null, null)).buildAndExpand(pathVariables); + } + if (methodName.equals(CREATEFORM)) { + return SpringletsMvcUriComponentsBuilder.fromMethodCall(SpringletsMvcUriComponentsBuilder.on(getControllerClass()).createForm(null)).buildAndExpand(pathVariables); + } + if (methodName.equals(DELETEBATCH)) { + return SpringletsMvcUriComponentsBuilder.fromMethodCall(SpringletsMvcUriComponentsBuilder.on(getControllerClass()).deleteBatch(null)).buildAndExpand(pathVariables); + } + if (methodName.equals(EXPORTCSV)) { + return SpringletsMvcUriComponentsBuilder.fromMethodCall(SpringletsMvcUriComponentsBuilder.on(getControllerClass()).exportCsv(null, null, null, null, null)).buildAndExpand(pathVariables); + } + if (methodName.equals(EXPORTPDF)) { + return SpringletsMvcUriComponentsBuilder.fromMethodCall(SpringletsMvcUriComponentsBuilder.on(getControllerClass()).exportPdf(null, null, null, null, null)).buildAndExpand(pathVariables); + } + if (methodName.equals(EXPORTXLS)) { + return SpringletsMvcUriComponentsBuilder.fromMethodCall(SpringletsMvcUriComponentsBuilder.on(getControllerClass()).exportXls(null, null, null, null, null)).buildAndExpand(pathVariables); + } + throw new IllegalArgumentException("Invalid method name: " + methodName); + } + +} diff --git a/spring-roo/src/main/java/com/baeldung/web/BooksItemThymeleafController.java b/spring-roo/src/main/java/com/baeldung/web/BooksItemThymeleafController.java new file mode 100644 index 000000000000..f16cb7be2d65 --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/web/BooksItemThymeleafController.java @@ -0,0 +1,15 @@ +package com.baeldung.web; +import com.baeldung.domain.Book; +import org.springframework.roo.addon.web.mvc.controller.annotations.ControllerType; +import org.springframework.roo.addon.web.mvc.controller.annotations.RooController; +import org.springframework.roo.addon.web.mvc.thymeleaf.annotations.RooThymeleaf; + +/** + * = BooksItemThymeleafController + TODO Auto-generated class documentation + * + */ +@RooController(entity = Book.class, type = ControllerType.ITEM) +@RooThymeleaf +public class BooksItemThymeleafController { +} diff --git a/spring-roo/src/main/java/com/baeldung/web/BooksItemThymeleafController_Roo_Controller.aj b/spring-roo/src/main/java/com/baeldung/web/BooksItemThymeleafController_Roo_Controller.aj new file mode 100644 index 000000000000..c0a4ed46df83 --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/web/BooksItemThymeleafController_Roo_Controller.aj @@ -0,0 +1,35 @@ +// WARNING: DO NOT EDIT THIS FILE. THIS FILE IS MANAGED BY SPRING ROO. +// You may push code into the target .java compilation unit if you wish to edit any member(s). + +package com.baeldung.web; + +import com.baeldung.service.api.BookService; +import com.baeldung.web.BooksItemThymeleafController; + +privileged aspect BooksItemThymeleafController_Roo_Controller { + + /** + * TODO Auto-generated attribute documentation + * + */ + private BookService BooksItemThymeleafController.bookService; + + /** + * TODO Auto-generated method documentation + * + * @return BookService + */ + public BookService BooksItemThymeleafController.getBookService() { + return bookService; + } + + /** + * TODO Auto-generated method documentation + * + * @param bookService + */ + public void BooksItemThymeleafController.setBookService(BookService bookService) { + this.bookService = bookService; + } + +} diff --git a/spring-roo/src/main/java/com/baeldung/web/BooksItemThymeleafController_Roo_Thymeleaf.aj b/spring-roo/src/main/java/com/baeldung/web/BooksItemThymeleafController_Roo_Thymeleaf.aj new file mode 100644 index 000000000000..502c30a81e4e --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/web/BooksItemThymeleafController_Roo_Thymeleaf.aj @@ -0,0 +1,251 @@ +// WARNING: DO NOT EDIT THIS FILE. THIS FILE IS MANAGED BY SPRING ROO. +// You may push code into the target .java compilation unit if you wish to edit any member(s). + +package com.baeldung.web; + +import com.baeldung.domain.Book; +import com.baeldung.service.api.BookService; +import com.baeldung.web.BooksItemThymeleafController; +import com.baeldung.web.BooksItemThymeleafLinkFactory; +import io.springlets.web.NotFoundException; +import io.springlets.web.mvc.util.ControllerMethodLinkBuilderFactory; +import io.springlets.web.mvc.util.MethodLinkBuilderFactory; +import java.util.Locale; +import javax.validation.Valid; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.InitBinder; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.util.UriComponents; + +privileged aspect BooksItemThymeleafController_Roo_Thymeleaf { + + declare @type: BooksItemThymeleafController: @Controller; + + declare @type: BooksItemThymeleafController: @RequestMapping(value = "/books/{book}", name = "BooksItemThymeleafController", produces = MediaType.TEXT_HTML_VALUE); + + /** + * TODO Auto-generated attribute documentation + * + */ + private MessageSource BooksItemThymeleafController.messageSource; + + /** + * TODO Auto-generated attribute documentation + * + */ + private MethodLinkBuilderFactory BooksItemThymeleafController.itemLink; + + /** + * TODO Auto-generated constructor documentation + * + * @param bookService + * @param messageSource + * @param linkBuilder + */ + @Autowired + public BooksItemThymeleafController.new(BookService bookService, MessageSource messageSource, ControllerMethodLinkBuilderFactory linkBuilder) { + setBookService(bookService); + setMessageSource(messageSource); + setItemLink(linkBuilder.of(BooksItemThymeleafController.class)); + } + + /** + * TODO Auto-generated method documentation + * + * @return MessageSource + */ + public MessageSource BooksItemThymeleafController.getMessageSource() { + return messageSource; + } + + /** + * TODO Auto-generated method documentation + * + * @param messageSource + */ + public void BooksItemThymeleafController.setMessageSource(MessageSource messageSource) { + this.messageSource = messageSource; + } + + /** + * TODO Auto-generated method documentation + * + * @return MethodLinkBuilderFactory + */ + public MethodLinkBuilderFactory BooksItemThymeleafController.getItemLink() { + return itemLink; + } + + /** + * TODO Auto-generated method documentation + * + * @param itemLink + */ + public void BooksItemThymeleafController.setItemLink(MethodLinkBuilderFactory itemLink) { + this.itemLink = itemLink; + } + + /** + * TODO Auto-generated method documentation + * + * @param id + * @param locale + * @param method + * @return Book + */ + @ModelAttribute + public Book BooksItemThymeleafController.getBook(@PathVariable("book") Long id, Locale locale, HttpMethod method) { + Book book = null; + if (HttpMethod.PUT.equals(method)) { + book = bookService.findOneForUpdate(id); + } else { + book = bookService.findOne(id); + } + + if (book == null) { + String message = messageSource.getMessage("error_NotFound", new Object[] {"Book", id}, "The record couldn't be found", locale); + throw new NotFoundException(message); + } + return book; + } + + /** + * TODO Auto-generated method documentation + * + * @param book + * @param model + * @return ModelAndView + */ + @GetMapping(name = "show") + public ModelAndView BooksItemThymeleafController.show(@ModelAttribute Book book, Model model) { + model.addAttribute("book", book); + return new ModelAndView("books/show"); + } + + /** + * TODO Auto-generated method documentation + * + * @param book + * @param model + * @return ModelAndView + */ + @GetMapping(value = "/inline", name = "showInline") + public ModelAndView BooksItemThymeleafController.showInline(@ModelAttribute Book book, Model model) { + model.addAttribute("book", book); + return new ModelAndView("books/showInline :: inline-content"); + } + + /** + * TODO Auto-generated method documentation + * + * @param dataBinder + */ + @InitBinder("book") + public void BooksItemThymeleafController.initBookBinder(WebDataBinder dataBinder) { + dataBinder.setDisallowedFields("id"); + } + + /** + * TODO Auto-generated method documentation + * + * @param model + */ + public void BooksItemThymeleafController.populateFormats(Model model) { + model.addAttribute("application_locale", LocaleContextHolder.getLocale().getLanguage()); + } + + /** + * TODO Auto-generated method documentation + * + * @param model + */ + public void BooksItemThymeleafController.populateForm(Model model) { + populateFormats(model); + } + + /** + * TODO Auto-generated method documentation + * + * @param book + * @param model + * @return ModelAndView + */ + @GetMapping(value = "/edit-form", name = "editForm") + public ModelAndView BooksItemThymeleafController.editForm(@ModelAttribute Book book, Model model) { + populateForm(model); + + model.addAttribute("book", book); + return new ModelAndView("books/edit"); + } + + /** + * TODO Auto-generated method documentation + * + * @param book + * @param version + * @param concurrencyControl + * @param result + * @param model + * @return ModelAndView + */ + @PutMapping(name = "update") + public ModelAndView BooksItemThymeleafController.update(@Valid @ModelAttribute Book book, @RequestParam("version") Integer version, @RequestParam(value = "concurrency", required = false, defaultValue = "") String concurrencyControl, BindingResult result, Model model) { + // Check if provided form contain errors + if (result.hasErrors()) { + populateForm(model); + + return new ModelAndView("books/edit"); + } + // Concurrency control + Book existingBook = getBookService().findOne(book.getId()); + if(book.getVersion() != existingBook.getVersion() && StringUtils.isEmpty(concurrencyControl)){ + populateForm(model); + model.addAttribute("book", book); + model.addAttribute("concurrency", true); + return new ModelAndView("books/edit"); + } else if(book.getVersion() != existingBook.getVersion() && "discard".equals(concurrencyControl)){ + populateForm(model); + model.addAttribute("book", existingBook); + model.addAttribute("concurrency", false); + return new ModelAndView("books/edit"); + } else if(book.getVersion() != existingBook.getVersion() && "apply".equals(concurrencyControl)){ + // Update the version field to be able to override the existing values + book.setVersion(existingBook.getVersion()); + } + Book savedBook = getBookService().save(book); + UriComponents showURI = getItemLink().to(BooksItemThymeleafLinkFactory.SHOW).with("book", savedBook.getId()).toUri(); + return new ModelAndView("redirect:" + showURI.toUriString()); + } + + /** + * TODO Auto-generated method documentation + * + * @param book + * @return ResponseEntity + */ + @ResponseBody + @DeleteMapping(name = "delete") + public ResponseEntity BooksItemThymeleafController.delete(@ModelAttribute Book book) { + getBookService().delete(book); + return ResponseEntity.ok().build(); + } + +} diff --git a/spring-roo/src/main/java/com/baeldung/web/BooksItemThymeleafLinkFactory.java b/spring-roo/src/main/java/com/baeldung/web/BooksItemThymeleafLinkFactory.java new file mode 100644 index 000000000000..5b6930686890 --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/web/BooksItemThymeleafLinkFactory.java @@ -0,0 +1,11 @@ +package com.baeldung.web; +import org.springframework.roo.addon.web.mvc.thymeleaf.annotations.RooLinkFactory; + +/** + * = BooksItemThymeleafLinkFactory + TODO Auto-generated class documentation + * + */ +@RooLinkFactory(controller = BooksItemThymeleafController.class) +public class BooksItemThymeleafLinkFactory { +} diff --git a/spring-roo/src/main/java/com/baeldung/web/BooksItemThymeleafLinkFactory_Roo_LinkFactory.aj b/spring-roo/src/main/java/com/baeldung/web/BooksItemThymeleafLinkFactory_Roo_LinkFactory.aj new file mode 100644 index 000000000000..fd6886658709 --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/web/BooksItemThymeleafLinkFactory_Roo_LinkFactory.aj @@ -0,0 +1,86 @@ +// WARNING: DO NOT EDIT THIS FILE. THIS FILE IS MANAGED BY SPRING ROO. +// You may push code into the target .java compilation unit if you wish to edit any member(s). + +package com.baeldung.web; + +import com.baeldung.web.BooksItemThymeleafController; +import com.baeldung.web.BooksItemThymeleafLinkFactory; +import io.springlets.web.mvc.util.MethodLinkFactory; +import io.springlets.web.mvc.util.SpringletsMvcUriComponentsBuilder; +import java.util.Map; +import org.springframework.stereotype.Component; +import org.springframework.web.util.UriComponents; + +privileged aspect BooksItemThymeleafLinkFactory_Roo_LinkFactory { + + declare parents: BooksItemThymeleafLinkFactory implements MethodLinkFactory; + + declare @type: BooksItemThymeleafLinkFactory: @Component; + + /** + * TODO Auto-generated attribute documentation + * + */ + public static final String BooksItemThymeleafLinkFactory.SHOW = "show"; + + /** + * TODO Auto-generated attribute documentation + * + */ + public static final String BooksItemThymeleafLinkFactory.SHOWINLINE = "showInline"; + + /** + * TODO Auto-generated attribute documentation + * + */ + public static final String BooksItemThymeleafLinkFactory.EDITFORM = "editForm"; + + /** + * TODO Auto-generated attribute documentation + * + */ + public static final String BooksItemThymeleafLinkFactory.UPDATE = "update"; + + /** + * TODO Auto-generated attribute documentation + * + */ + public static final String BooksItemThymeleafLinkFactory.DELETE = "delete"; + + /** + * TODO Auto-generated method documentation + * + * @return Class + */ + public Class BooksItemThymeleafLinkFactory.getControllerClass() { + return BooksItemThymeleafController.class; + } + + /** + * TODO Auto-generated method documentation + * + * @param methodName + * @param parameters + * @param pathVariables + * @return UriComponents + */ + public UriComponents BooksItemThymeleafLinkFactory.toUri(String methodName, Object[] parameters, Map pathVariables) { + if (methodName.equals(SHOW)) { + return SpringletsMvcUriComponentsBuilder.fromMethodCall(SpringletsMvcUriComponentsBuilder.on(getControllerClass()).show(null, null)).buildAndExpand(pathVariables); + } + if (methodName.equals(SHOWINLINE)) { + return SpringletsMvcUriComponentsBuilder.fromMethodCall(SpringletsMvcUriComponentsBuilder.on(getControllerClass()).showInline(null, null)).buildAndExpand(pathVariables); + } + if (methodName.equals(EDITFORM)) { + return SpringletsMvcUriComponentsBuilder.fromMethodCall(SpringletsMvcUriComponentsBuilder.on(getControllerClass()).editForm(null, null)).buildAndExpand(pathVariables); + } + if (methodName.equals(UPDATE)) { + return SpringletsMvcUriComponentsBuilder.fromMethodCall(SpringletsMvcUriComponentsBuilder.on(getControllerClass()).update(null, null, null, null, null)).buildAndExpand(pathVariables); + } + if (methodName.equals(DELETE)) { + return SpringletsMvcUriComponentsBuilder.fromMethodCall(SpringletsMvcUriComponentsBuilder.on(getControllerClass()).delete(null)).buildAndExpand(pathVariables); + } + throw new IllegalArgumentException("Invalid method name: " + methodName); + } + +} diff --git a/spring-roo/src/main/java/com/baeldung/web/MainController.java b/spring-roo/src/main/java/com/baeldung/web/MainController.java new file mode 100644 index 000000000000..bf2038b4a1b1 --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/web/MainController.java @@ -0,0 +1,11 @@ +package com.baeldung.web; +import org.springframework.roo.addon.web.mvc.thymeleaf.annotations.RooThymeleafMainController; + +/** + * = MainController + TODO Auto-generated class documentation + * + */ +@RooThymeleafMainController +public class MainController { +} diff --git a/spring-roo/src/main/java/com/baeldung/web/MainController_Roo_Thymeleaf_MainController.aj b/spring-roo/src/main/java/com/baeldung/web/MainController_Roo_Thymeleaf_MainController.aj new file mode 100644 index 000000000000..58f0466ed9b7 --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/web/MainController_Roo_Thymeleaf_MainController.aj @@ -0,0 +1,59 @@ +// WARNING: DO NOT EDIT THIS FILE. THIS FILE IS MANAGED BY SPRING ROO. +// You may push code into the target .java compilation unit if you wish to edit any member(s). + +package com.baeldung.web; + +import com.baeldung.web.MainController; +import io.springlets.web.NotFoundException; +import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +privileged aspect MainController_Roo_Thymeleaf_MainController { + + declare @type: MainController: @Controller; + + /** + * TODO Auto-generated method documentation + * + * @param model + * @return String + */ + @GetMapping("/") + public String MainController.index(Model model) { + model.addAttribute("application_locale", LocaleContextHolder.getLocale().getLanguage()); + return "index"; + } + + /** + * TODO Auto-generated method documentation + * + * @param model + * @return String + */ + @GetMapping("/accessibility") + public String MainController.accessibility(Model model) { + model.addAttribute("application_locale", LocaleContextHolder.getLocale().getLanguage()); + return "accessibility"; + } + + /** + * TODO Auto-generated method documentation + * + * @param template + * @return String + */ + @RequestMapping(value = "/js/{template}.js", method = RequestMethod.GET) + public String MainController.javascriptTemplates(@PathVariable("template") String template) { + if (StringUtils.hasLength(template)) { + return template.concat(".js"); + } + throw new NotFoundException("File not found"); + } + +} diff --git a/spring-roo/src/main/java/com/baeldung/web/reports/ExportingErrorException.java b/spring-roo/src/main/java/com/baeldung/web/reports/ExportingErrorException.java new file mode 100644 index 000000000000..a8c2eca5cc12 --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/web/reports/ExportingErrorException.java @@ -0,0 +1,19 @@ +package com.baeldung.web.reports; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) +public class ExportingErrorException extends RuntimeException { + + private static final long serialVersionUID = 4075788919321977605L; + + public ExportingErrorException() { + super("Error while trying to export data to file."); + } + + public ExportingErrorException(String message) { + super(message); + } + +} diff --git a/spring-roo/src/main/java/com/baeldung/web/reports/JasperReportsCsvExporter.java b/spring-roo/src/main/java/com/baeldung/web/reports/JasperReportsCsvExporter.java new file mode 100644 index 000000000000..5ea23e16ccf7 --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/web/reports/JasperReportsCsvExporter.java @@ -0,0 +1,71 @@ +package com.baeldung.web.reports; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletResponse; + +import net.sf.jasperreports.engine.JRException; +import net.sf.jasperreports.engine.JRExporterParameter; +import net.sf.jasperreports.engine.JasperPrint; +import net.sf.jasperreports.engine.export.JRCsvExporter; + +/** + * = JasperReportsCsvExporter + * + * A JasperReports exporter to export the report in CSV formats. This class + * implements the interface {@link JasperReportsExporter} + */ +public class JasperReportsCsvExporter implements JasperReportsExporter { + + /** + * Generates a ByteArrayOutputStream from the provided JasperReport using + * the {@link JRCsvExporter}. After that, the generated bytes array is + * written in the {@link HttpServletResponse} + * + * @param jp + * The generated JasperReport. + * @param fileName + * The fileName of the exported JasperReport + * @param response + * The HttpServletResponse where generated report has been + * written + * @throws JRException + * during JasperReport export. + * @throws IOException + * when writes the ByteArrayOutputStream into the + * HttpServletResponse + */ + @Override + public void export(JasperPrint jp, String fileName, HttpServletResponse response) throws JRException, IOException { + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + // Create a JRCsvExporter instance + JRCsvExporter exporter = new JRCsvExporter(); + + // Here we assign the parameters jp and baos to the exporter + exporter.setParameter(JRExporterParameter.JASPER_PRINT, jp); + exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, baos); + + // Retrieve the exported report in PDF format + exporter.exportReport(); + + // Specifies the response header + response.setHeader("Content-Disposition", "inline; filename=" + fileName); + + // Make sure to set the correct content type + // Each format has its own content type + response.setContentType("text/csv"); + response.setContentLength(baos.size()); + + // Retrieve the output stream + ServletOutputStream outputStream = response.getOutputStream(); + // Write to the output stream + baos.writeTo(outputStream); + // Flush the stream + outputStream.flush(); + + } +} \ No newline at end of file diff --git a/spring-roo/src/main/java/com/baeldung/web/reports/JasperReportsExporter.java b/spring-roo/src/main/java/com/baeldung/web/reports/JasperReportsExporter.java new file mode 100644 index 000000000000..32892df447ee --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/web/reports/JasperReportsExporter.java @@ -0,0 +1,41 @@ +package com.baeldung.web.reports; + +import java.io.IOException; + +import javax.servlet.http.HttpServletResponse; + +import net.sf.jasperreports.engine.JRException; +import net.sf.jasperreports.engine.JasperPrint; +import net.sf.jasperreports.export.Exporter; + +/** + * = JasperReportsExporter + * + * This interface defines the operations for a JasperReport exporter. + * + * JasperReports library already provides an Exporter interface called + * {@link Exporter}. However, it doesn't provides an operation that writes the + * exported JasperReport into the {@link HttpServletResponse}. + */ +public interface JasperReportsExporter { + + /** + * This operation must be implemented by every JasperReport exporter to be + * able to write a generated report into a the {@link HttpServletResponse}}. + * + * @param jp + * The generated JasperReport. + * @param fileName + * The fileName of the exported JasperReport + * @param response + * The HttpServletResponse where generated report has been + * written + * @throws JRException + * during JasperReport export. + * @throws IOException + * when writes the ByteArrayOutputStream into the + * HttpServletResponse + */ + public void export(JasperPrint jp, String fileName, HttpServletResponse response) throws JRException, IOException; + +} \ No newline at end of file diff --git a/spring-roo/src/main/java/com/baeldung/web/reports/JasperReportsPdfExporter.java b/spring-roo/src/main/java/com/baeldung/web/reports/JasperReportsPdfExporter.java new file mode 100644 index 000000000000..ae1e2d8ee03d --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/web/reports/JasperReportsPdfExporter.java @@ -0,0 +1,71 @@ +package com.baeldung.web.reports; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletResponse; + +import net.sf.jasperreports.engine.JRException; +import net.sf.jasperreports.engine.JRExporterParameter; +import net.sf.jasperreports.engine.JasperPrint; +import net.sf.jasperreports.engine.export.JRPdfExporter; + +/** + * = JasperReportsPdfExporter + * + * A JasperReports exporter to export the report in PDF formats. This class + * implements the interface {@link JasperReportsExporter} + */ +public class JasperReportsPdfExporter implements JasperReportsExporter { + + /** + * Generates a ByteArrayOutputStream from the provided JasperReport using + * the {@link JRPdfExporter}. After that, the generated bytes array is + * written in the {@link HttpServletResponse} + * + * @param jp + * The generated JasperReport. + * @param fileName + * The fileName of the exported JasperReport + * @param response + * The HttpServletResponse where generated report has been + * written + * @throws JRException + * during JasperReport export. + * @throws IOException + * when writes the ByteArrayOutputStream into the + * HttpServletResponse + */ + @Override + public void export(JasperPrint jp, String fileName, HttpServletResponse response) throws JRException, IOException { + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + // Create a JRPdfExporter instance + JRPdfExporter exporter = new JRPdfExporter(); + + // Here we assign the parameters jp and baos to the exporter + exporter.setParameter(JRExporterParameter.JASPER_PRINT, jp); + exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, baos); + + // Retrieve the exported report in PDF format + exporter.exportReport(); + + // Specifies the response header + response.setHeader("Content-Disposition", "inline; filename=" + fileName); + + // Make sure to set the correct content type + // Each format has its own content type + response.setContentType("application/pdf"); + response.setContentLength(baos.size()); + + // Retrieve the output stream + ServletOutputStream outputStream = response.getOutputStream(); + // Write to the output stream + baos.writeTo(outputStream); + // Flush the stream + outputStream.flush(); + + } +} \ No newline at end of file diff --git a/spring-roo/src/main/java/com/baeldung/web/reports/JasperReportsXlsExporter.java b/spring-roo/src/main/java/com/baeldung/web/reports/JasperReportsXlsExporter.java new file mode 100644 index 000000000000..1855d1c93ca0 --- /dev/null +++ b/spring-roo/src/main/java/com/baeldung/web/reports/JasperReportsXlsExporter.java @@ -0,0 +1,77 @@ +package com.baeldung.web.reports; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletResponse; + +import net.sf.jasperreports.engine.JRException; +import net.sf.jasperreports.engine.JRExporterParameter; +import net.sf.jasperreports.engine.JasperPrint; +import net.sf.jasperreports.engine.export.JRXlsAbstractExporterParameter; +import net.sf.jasperreports.engine.export.JRXlsExporter; + +/** + * = JasperReportsXlsExporter + * + * A JasperReports exporter to export the report in XLS formats. This class + * implements the interface {@link JasperReportsExporter} + */ +public class JasperReportsXlsExporter implements JasperReportsExporter { + + /** + * Generates a ByteArrayOutputStream from the provided JasperReport using + * the {@link JRXlsExporter}. After that, the generated bytes array is + * written in the {@link HttpServletResponse} + * + * @param jp + * The generated JasperReport. + * @param fileName + * The fileName of the exported JasperReport + * @param response + * The HttpServletResponse where generated report has been + * written + * @throws JRException + * during JasperReport export. + * @throws IOException + * when writes the ByteArrayOutputStream into the + * HttpServletResponse + */ + @Override + public void export(JasperPrint jp, String fileName, HttpServletResponse response) throws JRException, IOException { + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + // Create a JRXlsExporter instance + JRXlsExporter exporter = new JRXlsExporter(); + + // Here we assign the parameters jp and baos to the exporter + exporter.setParameter(JRExporterParameter.JASPER_PRINT, jp); + exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, baos); + + // Excel specific parameters + exporter.setParameter(JRXlsAbstractExporterParameter.IS_ONE_PAGE_PER_SHEET, Boolean.FALSE); + exporter.setParameter(JRXlsAbstractExporterParameter.IS_REMOVE_EMPTY_SPACE_BETWEEN_ROWS, Boolean.TRUE); + exporter.setParameter(JRXlsAbstractExporterParameter.IS_WHITE_PAGE_BACKGROUND, Boolean.FALSE); + + // Retrieve the exported report in PDF format + exporter.exportReport(); + + // Specifies the response header + response.setHeader("Content-Disposition", "inline; filename=" + fileName); + + // Make sure to set the correct content type + // Each format has its own content type + response.setContentType("application/vnd.ms-excel"); + response.setContentLength(baos.size()); + + // Retrieve the output stream + ServletOutputStream outputStream = response.getOutputStream(); + // Write to the output stream + baos.writeTo(outputStream); + // Flush the stream + outputStream.flush(); + + } +} \ No newline at end of file diff --git a/spring-roo/src/main/resources/application-dev.properties b/spring-roo/src/main/resources/application-dev.properties new file mode 100644 index 000000000000..6723cabefa9e --- /dev/null +++ b/spring-roo/src/main/resources/application-dev.properties @@ -0,0 +1,13 @@ +#Updated at Tue Apr 04 15:03:52 BST 2017 +#Tue Apr 04 15:03:52 BST 2017 +logging.file= +logging.level.com.baeldung=DEBUG +logging.level.com.querydsl.jpa.impl.JPAQuery=DEBUG +logging.level.org.hibernate.stat=DEBUG +logging.pattern.level=%5p - QP\:%X{querydsl.parameters} - +spring.jackson.serialization.indent-output=true +spring.jpa.properties.hibernate.format_sql=true +spring.jpa.properties.hibernate.generate_statistics=true +spring.jpa.show-sql=true +spring.messages.cache-seconds=0 +spring.thymeleaf.mode=html diff --git a/spring-roo/src/main/resources/application.properties b/spring-roo/src/main/resources/application.properties new file mode 100644 index 000000000000..77eaa5f3fc28 --- /dev/null +++ b/spring-roo/src/main/resources/application.properties @@ -0,0 +1,8 @@ +#Updated at Tue Apr 04 15:03:53 BST 2017 +#Tue Apr 04 15:03:53 BST 2017 +spring.datasource.driver-class-name=org.hsqldb.jdbcDriver +spring.datasource.url=jdbc\:hsqldb\:mem\:roo +spring.jpa.hibernate.naming.strategy=org.hibernate.cfg.ImprovedNamingStrategy +spring.messages.encoding=ISO-8859-1 +spring.messages.fallback-to-system-locale=false +spring.thymeleaf.mode=html diff --git a/spring-roo/src/main/resources/banner.txt b/spring-roo/src/main/resources/banner.txt new file mode 100644 index 000000000000..dd2932f7aa01 --- /dev/null +++ b/spring-roo/src/main/resources/banner.txt @@ -0,0 +1,9 @@ +${AnsiColor.GREEN} _ + ___ _ __ _ __(_)_ __ __ _ _ __ ___ ___ +/ __| '_ \| '__| | '_ \ / _` | | '__/ _ \ / _ \ +\__ \ |_) | | | | | | | (_| | | | | (_) | (_) | +|___/ .__/|_| |_|_| |_|\__, | |_| \___/ \___/ + |_| |___/ + +${AnsiColor.BRIGHT_RED}Spring application made with Spring Roo 2.0 +Doubts? http://projects.spring.io/spring-roo/${AnsiColor.DEFAULT} diff --git a/spring-roo/src/main/resources/messages.properties b/spring-roo/src/main/resources/messages.properties new file mode 100644 index 000000000000..e61a26650045 --- /dev/null +++ b/spring-roo/src/main/resources/messages.properties @@ -0,0 +1,146 @@ +#Updated at Tue Apr 04 15:04:02 BST 2017 +#Tue Apr 04 15:04:02 BST 2017 +error_NotFound={0} with identifier ''{1}'' not found. +error_NotNull=Required. +error_Size=The field must be between {2} and {1} characters long. +error_datatables_loading=An error happened while loading list data +error_deleting_item=Error deleting selected item. +error_deleting_item_with_relationships=To delete the selected item, must delete its related elements before. +error_expired_session=Your session has been expired +error_export_empty=No data available to generate a report +error_invalid_date=Please enter a correct date/time +error_invalid_maskValue=Please enter a valid value +error_login=Invalid user and password +help_login=Enter your login and password +info_author=Spring Roo development team +info_closed_session=Log out correctly +info_delete_batch_confirm=Are you sure want to delete the selected items? +info_delete_item_confirm=Are you sure want to delete this item? +info_deleted_item_problem=Error deleting item +info_deleted_items_batch=Deleted items +info_deleted_items_number=Deleted items {0} +info_description=Spring Roo, a next-generation rapid application development tool for Java developers. With Roo you can easily build full Java applications in minutes. +info_error=An unexpected error has occurred +info_homepage_header=With Roo you can easily build full Java applications in minutes. +info_homepage_paragraph=Spring Roo is a next-generation rapid application development tool for Java developers.\nIt focuses on higher productivity, stock-standard Java APIs, high usability, avoiding engineering trade-offs and \nfacilitating easy Roo removal. +info_homepage_project=Hello, this is your home page. +info_homepage_thanks=Thanks for your interest in Spring Roo\! +info_no_deleted_item=No deleted item. +info_no_exist_item=Item doesn't exist. +info_security_login=You tried to access a restricted area of our application. By default, you can log in with +info_select_an_option=Select an option +info_spring_code=Known, modify and redistribute the source code. +info_spring_documentation=If you are looking for Reference Documentation you can get it here. +info_spring_site=All the info about Spring Roo development. +info_spring_support=If you have any question about the project, \nyou can check it. +info_twitter=As always, you can find us also on +label_accessibility=Accessibility +label_accessibility_alt=Level Double-A conformance, W3C WAI Web Content Accessibility Guidelines 2.0 +label_accessibility_lead=Accessibility policy application +label_accessibility_text=

Spring Roo Application is committed to ensuring the accessibility of its web content to people with disabilities. All of the content on our website will meet W3C WAI's Web Content Accessibility Guidelines 2.0, Level AA conformance. Any issues should be reported to springroo@disid.com.

The technologies that is depended to access the accessible content are HTML, CSS and Javascript.

+label_accessibility_title=Explanation of WCAG 2.0 Level Double-A Conformance +label_actions=Actions +label_add_entity=Add {0} +label_back=Back +label_book=Book +label_book_author=Author +label_book_id=Id +label_book_isbn=Isbn +label_book_iterable_to_add_cant_be_null_message=I T E R A B L E_ T O_ A D D_ C A N T_ B E_ N U L L_ M E S S A G E +label_book_iterable_to_remove_cant_be_null_message=I T E R A B L E_ T O_ R E M O V E_ C A N T_ B E_ N U L L_ M E S S A G E +label_book_plural=Books +label_book_title=Title +label_book_version=Version +label_change_password=Change password +label_close=Close +label_code=Code +label_concurrency_apply=Apply my changes anyway +label_concurrency_apply_info=(discard all the changes applied by the other users). +label_concurrency_discard=Discard all my changes and reload this record. +label_concurrency_title=Warning\! This record has been updated by another user. +label_contact=Contact +label_create=Create +label_create_entity=Create {0} +label_data_entity={0} data +label_datatables_add=Add +label_datatables_columns=Columns +label_datatables_decimal=. +label_datatables_delete=Delete +label_datatables_emptyTable=No data available in table +label_datatables_first=First +label_datatables_info=Showing _START_ to _END_ of _TOTAL_ entries +label_datatables_infoEmpty=Showing 0 to 0 of 0 entries +label_datatables_infoFiltered=(filtered from _MAX_ total entries) +label_datatables_infoPostFix= +label_datatables_last=Last +label_datatables_lengthMenu=Show _MENU_ entries +label_datatables_loadingRecords=Loading... +label_datatables_loading_error=An error happened while loading list data +label_datatables_next=Next +label_datatables_previous=Previous +label_datatables_processing=Processing... +label_datatables_search=Search\: +label_datatables_selectedRow=1 selected row +label_datatables_selectedRows=%d selected rows +label_datatables_showRows=Show %d rows +label_datatables_sortAscending=\: activate to sort column ascending +label_datatables_sortDescending=\: activate to sort column descending +label_datatables_thousands= +label_datatables_zeroRecords=No matching records found +label_delete=Delete +label_delete_entity=Delete {0} +label_documentation=Doc +label_edit=Edit +label_edit_entity=Edit {0} +label_edit_search=Edit search +label_entities_found={0} found +label_error=Error +label_errorpage=Error page +label_errorpage_header=\u00C2\u00A1Error\! +label_exit=Exit +label_export_empty_error=Empty report +label_filtered_by=Filtered By +label_goBack=Go Back +label_goEdit=Go to the editing page +label_goHome=Go to homepage +label_gotoGithub=Go to Spring Roo page in GitHub +label_gotoLanguage=Display website in +label_gotowebsite=Go to Spring Roo website +label_help=Help +label_inputmask_groupSeparator=, +label_inputmask_prefix=$ +label_inputmask_radixPoint=. +label_inputmask_suffix= +label_last_access=Last Access {0} +label_list_entity=List {0} +label_list_of_entity=List of {0} +label_login=Login +label_login_password=Password +label_login_username=User +label_logout=Log out +label_menu_entry={0} +label_message=Message +label_not_filtered=Not Filtered +label_owasp_alt=Application developed and tested with OWASP +label_owasp_title=Application developed and tested with OWASP - Web Application Security Project +label_profile=Admin Profile +label_projectpage=Project Page +label_requiredfield=Required field +label_reset=Cancel +label_save=Save +label_search=Search +label_search_entity={0} searcher +label_show=Show +label_show_entity={0} card +label_spring_documentation=Spring Roo reference documentation +label_spring_site=Spring Roo project site +label_spring_sourcecode=Spring Roo source code +label_spring_support=Spring Roo Stackoverflow support +label_submit=Accept +label_support=Support +label_tools=Tools +label_user=User +language_label=Languages +language_label_en=English +language_label_es=Spanish +welcome_label=Welcome diff --git a/spring-roo/src/main/resources/static/public/css/springroo.css b/spring-roo/src/main/resources/static/public/css/springroo.css new file mode 100644 index 000000000000..2db8914ff1b4 --- /dev/null +++ b/spring-roo/src/main/resources/static/public/css/springroo.css @@ -0,0 +1,475 @@ +/* CSS Theme Roo */ + +body { + background-color: #f1f1f1; + color: rgba(13,13,13,.65); + line-height: 1.8; + letter-spacing: .01em; +} +img {border: 0; max-width: 100%; outline: none;} +a{ + color: #6db33f; + transition: color 150ms ease; + } +a:focus, a:hover{ + outline: none; + color: #3f6824; + } +.content a:hover {text-decoration: underline} +.content a.btn-action:hover, +.content .paginate_button a:hover, +.content a.btn:hover {text-decoration: none;} + +.clearfix {clear: both;} + +/*--NAVBAR-------------*/ + +.navbar-header .organization-logo {vertical-align: middle; padding: 0.2em 1em;} +.navbar-header .application-name { color: #34302d;} +.navbar-header .application-name strong {} +.navbar-default .navbar-header .application-name a {text-decoration: none;} +.navbar-inverse .navbar-header .application-name a {color: #fff; text-decoration: none;} +.navbar-header .application-name small {} + +.dropdown-menu > li button.btn-link { + display: block; + padding: 3px 20px; + white-space: nowrap; + color: #333; + width: 100%; + text-align: left; +} +.dropdown-menu > li button.btn-link:hover, +.dropdown-menu > li button.btn-link:focus { + color: #262626; + text-decoration: none; + background-color: #f5f5f5; +} +.dropdown-menu > .active button.btn-link, +.dropdown-menu > .active button.btn-link:hover, +.dropdown-menu > .active button.btn-link:focus { + color: #262626; + text-decoration: none; + background-color: #f5f5f5; +} + +/*--FOOTER-------------*/ + +footer { padding: 1em 0 3em; text-align: center;} +footer p{font-size: 0.875em;} +footer a:hover, footer a:focus{text-decoration: none;} + + + +/*--HEADINGS-------------*/ + +.content h4,h5,h6 {color: #34302d;} + +/*--CONTENT-------------*/ + +/* second container */ +.content { + clear: both; + min-height: 26.3em; + display: block; + padding: 1em; + background: #fff; + box-shadow: 0 0 0 0,0 6px 12px rgba(34,34,34,.1); +} +.content > .panel{ +/* margin: 1em; */ +} + +/*--SHOW-------------*/ + +.content ul.list-unstyled li{ + margin: 0.25em 0; +} +.content ul.list-unstyled li{ + padding: 0.25em 0; +} +.content ul.list-unstyled li strong{ + min-width: 15%; + display: inline-block; + } +.content ul.list-unstyled li span{ + display: inline-block; + padding-left: 1em; +} +.content [id$="FieldSet"] h2{ + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: inherit; + color: inherit; + border-bottom: 1px solid #f1f1f1; +} +dd, dt { + margin: 0px 5px 5px 0px; +} + +/*--ALERTS-------------*/ + +.alert blockquote{ + margin: 0; + font-size: inherit; +} +.alert-info blockquote { + border-left-color: #31708f; +} +/*--FORMS-------------*/ + +legend{ + border-bottom: 1px solid #f1f1f1; +} +.form-horizontal .form-group input[type="radio"], +.form-horizontal .form-group input[type="checkbox"] { + margin-left: 0; +} + +/*--TABLES-------------*/ + +table.dataTable thead .sorting, +table.dataTable thead .sorting_asc, +table.dataTable thead .sorting_desc { + background-image: none; +} + +/** toolbars datatables **/ +div.dataTables_wrapper div.dataTables_paginate, +div.dataTables_wrapper div.dataTables_info{ + padding: 0; +} + +div.dataTables_wrapper div.dataTables_filter, +div.dataTables_wrapper div.dt-buttons.btn-group{ + padding: 1em 0; +} +.pagination{ + text-transform: none; +} +.dataTables_wrapper .dataTables_paginate .paginate_button{ + padding: 0; +} +.dataTables_wrapper .dataTables_paginate .paginate_button:hover { + background: none; +} +/** btn datatables **/ +table a.btn-action { + text-decoration: none; + color: #3e3f3a; +} +table .btn-action + .btn-action { + margin: 0 0 0 0.35em; +} +table a.btn-action:hover, +table a.btn-action:focus, +table a.btn-action:active { + color: #79a736; +} +table.dataTable.no-footer { + border-bottom: 1px solid #dfd7ca; +} + +div.dataTables_wrapper div.dataTables_processing { + position: absolute; + top: 50%; + left: 50%; + width: 200px; + margin-left: -100px; + margin-top: 16px; + padding: 0.5em 0; + height: 30px; + overflow: hidden; + border-radius: 4px; + -moz-animation: progress-bar-stripes 2s linear infinite; + -ms-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-size: 40px 40px; + font-size: 12px; + color: #ffffff; + text-align: center; + background-color: #428bca; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -webkit-transition: width 0.6s ease; + transition: width 0.6s ease; +} +/*--BTN-------------*/ + +.btn-action:before { + font-family: 'FontAwesome'; + padding: 0 0.3em 0 0; +} +.btn-action.delete:before { + content:"\f014"; + } +.btn-action.edit:before, +.btn-action.modify:before { + content:"\f040"; + } +.btn-action.accept:before { + content:"\f00c"; + } +.btn-action.add:before { + content:"\f067"; + } +.btn-action.showInfo:before { + content:"\f06e"; +} +.alert a.btn{ + color: #555555; + text-decoration: none; +} + +/*-------------------INDEX-------------------*/ + +body.home .content{ + background: none; + padding: 0; + box-shadow: none; +} +body.home .box-center{ + padding: 1em 2em; + background: #fff; + box-shadow: 0 0 0 0,0 6px 12px rgba(34,34,34,.1); +} +body.home .navbar { margin-bottom: 0px;} + +/*--HEADER-------------*/ +.jumbotron { + min-height: 6.5em; + text-align: center; + vertical-align: middle; + margin-bottom: 2em; + background-color: #316128; + background-image: linear-gradient(120deg, #316128, #254211); + border-bottom: 1px solid #969595; + border: none; +} +.jumbotron.bg-banner { + min-height: 20em; + color: #fff; + text-align: center; + padding: 3em; + background-image: url(../img/geo.png); + background-repeat: no-repeat; + background-position: 50% 20%; + -webkit-background-size: cover; + -moz-background-size: cover; + -o-background-size: cover; + background-size: cover; +} +.jumbotron .project-name{ + color: #fff; + text-align: center; + font-size: 4em; +} +.jumbotron .project-tagline { + color: #fff; + margin-bottom: 2rem; + font-weight: normal; + opacity: 0.7; +} + +/*--SOCIAL LINKS-------------*/ + +.social-links a{ + color: #999; + text-decoration: none; + font-size: 1.5em; + margin: 0 0.15em; +} + +/*--BLOCKS----------------*/ + +.blocks { margin-top: 2em;} +.block-item { + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15); + position: relative; + margin-bottom: 30px; +} +.block-item .doc { + padding: 15px; + float: left; + font-size: 4em; +} +.block-item a{ + color: #fff; + cursor: pointer; +} +.block-item .text { + overflow: hidden; + color: rgba(255, 255, 255, 0.9); + padding: 16px 12px; +} +.block-item .text > h2 { + margin: 0; + line-height: 100%; + font-size: 22px; + font-weight: 300; + color: #fff; +} +.block-item .text > small { + margin-bottom: 2px; + display: block; +} +.block-item .text > p, +.block-item .text > h2 { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.block-item > .clearfix, +.block-item > .dl-horizontal dd, +.block-item > .container, +.block-item > .container-fluid, +.block-item > .row, +.block-item > .form-horizontal .form-group, +.block-item > .btn-toolbar, +.block-item > .btn-group-vertical > .btn-group, +.block-item > .nav, +.block-item > .navbar, +.block-item > .navbar-header, +.block-item > .navbar-collapse, +.block-item > .pager, +.block-item > .panel-body, +.block-item > .modal-header, +.block-item > .modal-footer { + position: relative; + z-index: 1; +} +.block-item:before { + -webkit-transition: width; + -o-transition: width; + transition: width; + -webkit-transition-duration: 500ms; + transition-duration: 500ms; + -webkit-backface-visibility: hidden; + -moz-backface-visibility: hidden; + backface-visibility: hidden; + content: ""; + width: 83px; + height: 100%; + background: rgba(0, 0, 0, 0.1); + position: absolute; + left: 0; + top: 0; +} +.block-item:hover .text {color: #fff !important;} +.block-item:hover:before { width: 100%;} + +/* + * Material Background Colors + */ + +.bgm-teal { background-color: #009688 !important;} +.bgm-red { background-color: #f44336 !important;} +.bgm-amber { background-color: #ffc107 !important;} +.bgm-bluegray {background-color: #607d8b !important;} + + +/* SANDSTONE THEME CUSTOM +-----------------------------------------------------------------------------*/ + +/*-- NAVBAR -------------*/ + +.navbar-default .navbar-header .application-name a {color: #fff; text-decoration: none;} + +.dropdown-menu > li button.btn-link { + clear: both; + font-size: 11px; + line-height: 22px; + font-weight: 500; + text-transform: uppercase; + color: #98978b; +} +.dropdown-menu > li button.btn-link:hover, +.dropdown-menu > li button.btn-link:focus { + color: #98978b; + background-color: #f8f5f0; +} +.dropdown-menu > .active button.btn-link, +.dropdown-menu > .active button.btn-link:hover, +.dropdown-menu > .active button.btn-link:focus { + color: #98978b; + background-color: #f8f5f0; +} + +/*-- SELECT2 -------------*/ + +.select2-container--bootstrap .select2-selection{ + height: 46px; + padding: 12px 16px; + font-size: 14px; + line-height: 1.42857143; + color: #3e3f3a; + background-color: #ffffff; + background-image: none; + border: 1px solid #dfd7ca; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; +} +.select2-container--bootstrap .select2-selection--multiple { + padding: 6px; +} +.select2-container--bootstrap .select2-selection--multiple .select2-selection__arrow, +.select2-container--bootstrap .select2-selection--multiple .select2-selection__placeholder, +.select2-container--bootstrap .select2-selection--single .select2-selection__arrow, +.select2-container--bootstrap .select2-selection--single .select2-selection__placeholder{ + color: #dfd7ca; + padding: 0; +} +.select2-container--bootstrap .select2-selection--multiple .select2-selection__arrow b, +.select2-container--bootstrap .select2-selection--single .select2-selection__arrow b { + border-color: #dfd7ca transparent transparent transparent; +} +/* align options in left */ +.select2-container--bootstrap .select2-selection--multiple .select2-selection__rendered{ + padding-right: 0.5em !important; +} +.select2-container--bootstrap .select2-selection--multiple .select2-selection__clear { + margin-right: 0 !important; +} + +/* MEDIA QUERIES +-------------------------------------------------------------------------------*/ + +@media print { + body {background: #fff;} + .content .main {width: 98%;} +} + +@media (max-width: 768px) { + .jumbotron .project-name{font-size: 3em;} + /* navbar fixes */ + .navbar-nav#entitiesMenuEntries { + max-width: 410px; + } +} + +@media (max-width: 992px){ + .navbar-nav.upper-nav > li > a {padding: 15px 5px} +} + +@media (min-width: 1020px){ + /* navbar fixes */ + .navbar-nav#entitiesMenuEntries { + max-width: 370px; + } +} + +@media (min-width: 1100px){ + /* navbar fixes */ + .navbar-nav#entitiesMenuEntries { + max-width: 50%; + } +} diff --git a/spring-roo/src/main/resources/static/public/css/theme.css b/spring-roo/src/main/resources/static/public/css/theme.css new file mode 100644 index 000000000000..02722dd20c9c --- /dev/null +++ b/spring-roo/src/main/resources/static/public/css/theme.css @@ -0,0 +1,6914 @@ +@import url("https://fonts.googleapis.com/css?family=Roboto:400,500"); +/*! + * bootswatch v3.3.6 + * Homepage: http://bootswatch.com + * Copyright 2012-2015 Thomas Park + * Licensed under MIT + * Based on Bootstrap + * Sandstone theme +*/ +/*! + * Bootstrap v3.3.6 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ +html { + font-family: sans-serif; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; +} +body { + margin: 0; +} +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +menu, +nav, +section, +summary { + display: block; +} +audio, +canvas, +progress, +video { + display: inline-block; + vertical-align: baseline; +} +audio:not([controls]) { + display: none; + height: 0; +} +[hidden], +template { + display: none; +} +a { + background-color: transparent; +} +a:active, +a:hover { + outline: 0; +} +abbr[title] { + border-bottom: 1px dotted; +} +b, +strong { + font-weight: bold; +} +dfn { + font-style: italic; +} +h1 { + font-size: 2em; + margin: 0.67em 0; +} +/*mark { + background: #ff0; + color: #000; +}*/ +small { + font-size: 80%; +} +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} +sup { + top: -0.5em; +} +sub { + bottom: -0.25em; +} +img { + border: 0; +} +svg:not(:root) { + overflow: hidden; +} +figure { + margin: 1em 40px; +} +hr { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + height: 0; +} +pre { + overflow: auto; +} +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; +} +button, +input, +optgroup, +select, +textarea { + color: inherit; + font: inherit; + margin: 0; +} +button { + overflow: visible; +} +button, +select { + text-transform: none; +} +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; + cursor: pointer; +} +button[disabled], +html input[disabled] { + cursor: default; +} +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} +input { + line-height: normal; +} +input[type="checkbox"], +input[type="radio"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 0; +} +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} +input[type="search"] { + -webkit-appearance: textfield; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} +legend { + border: 0; + padding: 0; +} +textarea { + overflow: auto; +} +optgroup { + font-weight: bold; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +td, +th { + padding: 0; +} +/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ +@media print { + *, + *:before, + *:after { + background: transparent !important; + color: #000 !important; + -webkit-box-shadow: none !important; + box-shadow: none !important; + text-shadow: none !important; + } + a, + a:visited { + text-decoration: underline; + } + a[href]:after { + content: " (" attr(href) ")"; + } + abbr[title]:after { + content: " (" attr(title) ")"; + } + a[href^="#"]:after, + a[href^="javascript:"]:after { + content: ""; + } + pre, + blockquote { + border: 1px solid #999; + page-break-inside: avoid; + } + thead { + display: table-header-group; + } + tr, + img { + page-break-inside: avoid; + } + img { + max-width: 100% !important; + } + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + h2, + h3 { + page-break-after: avoid; + } + .navbar { + display: none; + } + .btn > .caret, + .dropup > .btn > .caret { + border-top-color: #000 !important; + } + .label { + border: 1px solid #000; + } + .table { + border-collapse: collapse !important; + } + .table td, + .table th { + background-color: #fff !important; + } + .table-bordered th, + .table-bordered td { + border: 1px solid #ddd !important; + } +} +/* @font-face { + font-family: 'Glyphicons Halflings'; + src: url('../fonts/glyphicons-halflings-regular.eot'); + src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); +} */ +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings'; + font-style: normal; + font-weight: normal; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.glyphicon-asterisk:before { + content: "\002a"; +} +.glyphicon-plus:before { + content: "\002b"; +} +.glyphicon-euro:before, +.glyphicon-eur:before { + content: "\20ac"; +} +.glyphicon-minus:before { + content: "\2212"; +} +.glyphicon-cloud:before { + content: "\2601"; +} +.glyphicon-envelope:before { + content: "\2709"; +} +.glyphicon-pencil:before { + content: "\270f"; +} +.glyphicon-glass:before { + content: "\e001"; +} +.glyphicon-music:before { + content: "\e002"; +} +.glyphicon-search:before { + content: "\e003"; +} +.glyphicon-heart:before { + content: "\e005"; +} +.glyphicon-star:before { + content: "\e006"; +} +.glyphicon-star-empty:before { + content: "\e007"; +} +.glyphicon-user:before { + content: "\e008"; +} +.glyphicon-film:before { + content: "\e009"; +} +.glyphicon-th-large:before { + content: "\e010"; +} +.glyphicon-th:before { + content: "\e011"; +} +.glyphicon-th-list:before { + content: "\e012"; +} +.glyphicon-ok:before { + content: "\e013"; +} +.glyphicon-remove:before { + content: "\e014"; +} +.glyphicon-zoom-in:before { + content: "\e015"; +} +.glyphicon-zoom-out:before { + content: "\e016"; +} +.glyphicon-off:before { + content: "\e017"; +} +.glyphicon-signal:before { + content: "\e018"; +} +.glyphicon-cog:before { + content: "\e019"; +} +.glyphicon-trash:before { + content: "\e020"; +} +.glyphicon-home:before { + content: "\e021"; +} +.glyphicon-file:before { + content: "\e022"; +} +.glyphicon-time:before { + content: "\e023"; +} +.glyphicon-road:before { + content: "\e024"; +} +.glyphicon-download-alt:before { + content: "\e025"; +} +.glyphicon-download:before { + content: "\e026"; +} +.glyphicon-upload:before { + content: "\e027"; +} +.glyphicon-inbox:before { + content: "\e028"; +} +.glyphicon-play-circle:before { + content: "\e029"; +} +.glyphicon-repeat:before { + content: "\e030"; +} +.glyphicon-refresh:before { + content: "\e031"; +} +.glyphicon-list-alt:before { + content: "\e032"; +} +.glyphicon-lock:before { + content: "\e033"; +} +.glyphicon-flag:before { + content: "\e034"; +} +.glyphicon-headphones:before { + content: "\e035"; +} +.glyphicon-volume-off:before { + content: "\e036"; +} +.glyphicon-volume-down:before { + content: "\e037"; +} +.glyphicon-volume-up:before { + content: "\e038"; +} +.glyphicon-qrcode:before { + content: "\e039"; +} +.glyphicon-barcode:before { + content: "\e040"; +} +.glyphicon-tag:before { + content: "\e041"; +} +.glyphicon-tags:before { + content: "\e042"; +} +.glyphicon-book:before { + content: "\e043"; +} +.glyphicon-bookmark:before { + content: "\e044"; +} +.glyphicon-print:before { + content: "\e045"; +} +.glyphicon-camera:before { + content: "\e046"; +} +.glyphicon-font:before { + content: "\e047"; +} +.glyphicon-bold:before { + content: "\e048"; +} +.glyphicon-italic:before { + content: "\e049"; +} +.glyphicon-text-height:before { + content: "\e050"; +} +.glyphicon-text-width:before { + content: "\e051"; +} +.glyphicon-align-left:before { + content: "\e052"; +} +.glyphicon-align-center:before { + content: "\e053"; +} +.glyphicon-align-right:before { + content: "\e054"; +} +.glyphicon-align-justify:before { + content: "\e055"; +} +.glyphicon-list:before { + content: "\e056"; +} +.glyphicon-indent-left:before { + content: "\e057"; +} +.glyphicon-indent-right:before { + content: "\e058"; +} +.glyphicon-facetime-video:before { + content: "\e059"; +} +.glyphicon-picture:before { + content: "\e060"; +} +.glyphicon-map-marker:before { + content: "\e062"; +} +.glyphicon-adjust:before { + content: "\e063"; +} +.glyphicon-tint:before { + content: "\e064"; +} +.glyphicon-edit:before { + content: "\e065"; +} +.glyphicon-share:before { + content: "\e066"; +} +.glyphicon-check:before { + content: "\e067"; +} +.glyphicon-move:before { + content: "\e068"; +} +.glyphicon-step-backward:before { + content: "\e069"; +} +.glyphicon-fast-backward:before { + content: "\e070"; +} +.glyphicon-backward:before { + content: "\e071"; +} +.glyphicon-play:before { + content: "\e072"; +} +.glyphicon-pause:before { + content: "\e073"; +} +.glyphicon-stop:before { + content: "\e074"; +} +.glyphicon-forward:before { + content: "\e075"; +} +.glyphicon-fast-forward:before { + content: "\e076"; +} +.glyphicon-step-forward:before { + content: "\e077"; +} +.glyphicon-eject:before { + content: "\e078"; +} +.glyphicon-chevron-left:before { + content: "\e079"; +} +.glyphicon-chevron-right:before { + content: "\e080"; +} +.glyphicon-plus-sign:before { + content: "\e081"; +} +.glyphicon-minus-sign:before { + content: "\e082"; +} +.glyphicon-remove-sign:before { + content: "\e083"; +} +.glyphicon-ok-sign:before { + content: "\e084"; +} +.glyphicon-question-sign:before { + content: "\e085"; +} +.glyphicon-info-sign:before { + content: "\e086"; +} +.glyphicon-screenshot:before { + content: "\e087"; +} +.glyphicon-remove-circle:before { + content: "\e088"; +} +.glyphicon-ok-circle:before { + content: "\e089"; +} +.glyphicon-ban-circle:before { + content: "\e090"; +} +.glyphicon-arrow-left:before { + content: "\e091"; +} +.glyphicon-arrow-right:before { + content: "\e092"; +} +.glyphicon-arrow-up:before { + content: "\e093"; +} +.glyphicon-arrow-down:before { + content: "\e094"; +} +.glyphicon-share-alt:before { + content: "\e095"; +} +.glyphicon-resize-full:before { + content: "\e096"; +} +.glyphicon-resize-small:before { + content: "\e097"; +} +.glyphicon-exclamation-sign:before { + content: "\e101"; +} +.glyphicon-gift:before { + content: "\e102"; +} +.glyphicon-leaf:before { + content: "\e103"; +} +.glyphicon-fire:before { + content: "\e104"; +} +.glyphicon-eye-open:before { + content: "\e105"; +} +.glyphicon-eye-close:before { + content: "\e106"; +} +.glyphicon-warning-sign:before { + content: "\e107"; +} +.glyphicon-plane:before { + content: "\e108"; +} +.glyphicon-calendar:before { + content: "\e109"; +} +.glyphicon-random:before { + content: "\e110"; +} +.glyphicon-comment:before { + content: "\e111"; +} +.glyphicon-magnet:before { + content: "\e112"; +} +.glyphicon-chevron-up:before { + content: "\e113"; +} +.glyphicon-chevron-down:before { + content: "\e114"; +} +.glyphicon-retweet:before { + content: "\e115"; +} +.glyphicon-shopping-cart:before { + content: "\e116"; +} +.glyphicon-folder-close:before { + content: "\e117"; +} +.glyphicon-folder-open:before { + content: "\e118"; +} +.glyphicon-resize-vertical:before { + content: "\e119"; +} +.glyphicon-resize-horizontal:before { + content: "\e120"; +} +.glyphicon-hdd:before { + content: "\e121"; +} +.glyphicon-bullhorn:before { + content: "\e122"; +} +.glyphicon-bell:before { + content: "\e123"; +} +.glyphicon-certificate:before { + content: "\e124"; +} +.glyphicon-thumbs-up:before { + content: "\e125"; +} +.glyphicon-thumbs-down:before { + content: "\e126"; +} +.glyphicon-hand-right:before { + content: "\e127"; +} +.glyphicon-hand-left:before { + content: "\e128"; +} +.glyphicon-hand-up:before { + content: "\e129"; +} +.glyphicon-hand-down:before { + content: "\e130"; +} +.glyphicon-circle-arrow-right:before { + content: "\e131"; +} +.glyphicon-circle-arrow-left:before { + content: "\e132"; +} +.glyphicon-circle-arrow-up:before { + content: "\e133"; +} +.glyphicon-circle-arrow-down:before { + content: "\e134"; +} +.glyphicon-globe:before { + content: "\e135"; +} +.glyphicon-wrench:before { + content: "\e136"; +} +.glyphicon-tasks:before { + content: "\e137"; +} +.glyphicon-filter:before { + content: "\e138"; +} +.glyphicon-briefcase:before { + content: "\e139"; +} +.glyphicon-fullscreen:before { + content: "\e140"; +} +.glyphicon-dashboard:before { + content: "\e141"; +} +.glyphicon-paperclip:before { + content: "\e142"; +} +.glyphicon-heart-empty:before { + content: "\e143"; +} +.glyphicon-link:before { + content: "\e144"; +} +.glyphicon-phone:before { + content: "\e145"; +} +.glyphicon-pushpin:before { + content: "\e146"; +} +.glyphicon-usd:before { + content: "\e148"; +} +.glyphicon-gbp:before { + content: "\e149"; +} +.glyphicon-sort:before { + content: "\e150"; +} +.glyphicon-sort-by-alphabet:before { + content: "\e151"; +} +.glyphicon-sort-by-alphabet-alt:before { + content: "\e152"; +} +.glyphicon-sort-by-order:before { + content: "\e153"; +} +.glyphicon-sort-by-order-alt:before { + content: "\e154"; +} +.glyphicon-sort-by-attributes:before { + content: "\e155"; +} +.glyphicon-sort-by-attributes-alt:before { + content: "\e156"; +} +.glyphicon-unchecked:before { + content: "\e157"; +} +.glyphicon-expand:before { + content: "\e158"; +} +.glyphicon-collapse-down:before { + content: "\e159"; +} +.glyphicon-collapse-up:before { + content: "\e160"; +} +.glyphicon-log-in:before { + content: "\e161"; +} +.glyphicon-flash:before { + content: "\e162"; +} +.glyphicon-log-out:before { + content: "\e163"; +} +.glyphicon-new-window:before { + content: "\e164"; +} +.glyphicon-record:before { + content: "\e165"; +} +.glyphicon-save:before { + content: "\e166"; +} +.glyphicon-open:before { + content: "\e167"; +} +.glyphicon-saved:before { + content: "\e168"; +} +.glyphicon-import:before { + content: "\e169"; +} +.glyphicon-export:before { + content: "\e170"; +} +.glyphicon-send:before { + content: "\e171"; +} +.glyphicon-floppy-disk:before { + content: "\e172"; +} +.glyphicon-floppy-saved:before { + content: "\e173"; +} +.glyphicon-floppy-remove:before { + content: "\e174"; +} +.glyphicon-floppy-save:before { + content: "\e175"; +} +.glyphicon-floppy-open:before { + content: "\e176"; +} +.glyphicon-credit-card:before { + content: "\e177"; +} +.glyphicon-transfer:before { + content: "\e178"; +} +.glyphicon-cutlery:before { + content: "\e179"; +} +.glyphicon-header:before { + content: "\e180"; +} +.glyphicon-compressed:before { + content: "\e181"; +} +.glyphicon-earphone:before { + content: "\e182"; +} +.glyphicon-phone-alt:before { + content: "\e183"; +} +.glyphicon-tower:before { + content: "\e184"; +} +.glyphicon-stats:before { + content: "\e185"; +} +.glyphicon-sd-video:before { + content: "\e186"; +} +.glyphicon-hd-video:before { + content: "\e187"; +} +.glyphicon-subtitles:before { + content: "\e188"; +} +.glyphicon-sound-stereo:before { + content: "\e189"; +} +.glyphicon-sound-dolby:before { + content: "\e190"; +} +.glyphicon-sound-5-1:before { + content: "\e191"; +} +.glyphicon-sound-6-1:before { + content: "\e192"; +} +.glyphicon-sound-7-1:before { + content: "\e193"; +} +.glyphicon-copyright-mark:before { + content: "\e194"; +} +.glyphicon-registration-mark:before { + content: "\e195"; +} +.glyphicon-cloud-download:before { + content: "\e197"; +} +.glyphicon-cloud-upload:before { + content: "\e198"; +} +.glyphicon-tree-conifer:before { + content: "\e199"; +} +.glyphicon-tree-deciduous:before { + content: "\e200"; +} +.glyphicon-cd:before { + content: "\e201"; +} +.glyphicon-save-file:before { + content: "\e202"; +} +.glyphicon-open-file:before { + content: "\e203"; +} +.glyphicon-level-up:before { + content: "\e204"; +} +.glyphicon-copy:before { + content: "\e205"; +} +.glyphicon-paste:before { + content: "\e206"; +} +.glyphicon-alert:before { + content: "\e209"; +} +.glyphicon-equalizer:before { + content: "\e210"; +} +.glyphicon-king:before { + content: "\e211"; +} +.glyphicon-queen:before { + content: "\e212"; +} +.glyphicon-pawn:before { + content: "\e213"; +} +.glyphicon-bishop:before { + content: "\e214"; +} +.glyphicon-knight:before { + content: "\e215"; +} +.glyphicon-baby-formula:before { + content: "\e216"; +} +.glyphicon-tent:before { + content: "\26fa"; +} +.glyphicon-blackboard:before { + content: "\e218"; +} +.glyphicon-bed:before { + content: "\e219"; +} +.glyphicon-apple:before { + content: "\f8ff"; +} +.glyphicon-erase:before { + content: "\e221"; +} +.glyphicon-hourglass:before { + content: "\231b"; +} +.glyphicon-lamp:before { + content: "\e223"; +} +.glyphicon-duplicate:before { + content: "\e224"; +} +.glyphicon-piggy-bank:before { + content: "\e225"; +} +.glyphicon-scissors:before { + content: "\e226"; +} +.glyphicon-bitcoin:before { + content: "\e227"; +} +.glyphicon-btc:before { + content: "\e227"; +} +.glyphicon-xbt:before { + content: "\e227"; +} +.glyphicon-yen:before { + content: "\00a5"; +} +.glyphicon-jpy:before { + content: "\00a5"; +} +.glyphicon-ruble:before { + content: "\20bd"; +} +.glyphicon-rub:before { + content: "\20bd"; +} +.glyphicon-scale:before { + content: "\e230"; +} +.glyphicon-ice-lolly:before { + content: "\e231"; +} +.glyphicon-ice-lolly-tasted:before { + content: "\e232"; +} +.glyphicon-education:before { + content: "\e233"; +} +.glyphicon-option-horizontal:before { + content: "\e234"; +} +.glyphicon-option-vertical:before { + content: "\e235"; +} +.glyphicon-menu-hamburger:before { + content: "\e236"; +} +.glyphicon-modal-window:before { + content: "\e237"; +} +.glyphicon-oil:before { + content: "\e238"; +} +.glyphicon-grain:before { + content: "\e239"; +} +.glyphicon-sunglasses:before { + content: "\e240"; +} +.glyphicon-text-size:before { + content: "\e241"; +} +.glyphicon-text-color:before { + content: "\e242"; +} +.glyphicon-text-background:before { + content: "\e243"; +} +.glyphicon-object-align-top:before { + content: "\e244"; +} +.glyphicon-object-align-bottom:before { + content: "\e245"; +} +.glyphicon-object-align-horizontal:before { + content: "\e246"; +} +.glyphicon-object-align-left:before { + content: "\e247"; +} +.glyphicon-object-align-vertical:before { + content: "\e248"; +} +.glyphicon-object-align-right:before { + content: "\e249"; +} +.glyphicon-triangle-right:before { + content: "\e250"; +} +.glyphicon-triangle-left:before { + content: "\e251"; +} +.glyphicon-triangle-bottom:before { + content: "\e252"; +} +.glyphicon-triangle-top:before { + content: "\e253"; +} +.glyphicon-console:before { + content: "\e254"; +} +.glyphicon-superscript:before { + content: "\e255"; +} +.glyphicon-subscript:before { + content: "\e256"; +} +.glyphicon-menu-left:before { + content: "\e257"; +} +.glyphicon-menu-right:before { + content: "\e258"; +} +.glyphicon-menu-down:before { + content: "\e259"; +} +.glyphicon-menu-up:before { + content: "\e260"; +} +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +*:before, +*:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +html { + font-size: 10px; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} +body { + font-family: "Roboto", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.42857143; + color: #3e3f3a; + background-color: #ffffff; +} +input, +button, +select, +textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit; +} +a { + color: #93c54b; + text-decoration: none; +} +a:hover, +a:focus { + color: #79a736; + text-decoration: underline; +} +a:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +figure { + margin: 0; +} +img { + vertical-align: middle; +} +.img-responsive, +.thumbnail > img, +.thumbnail a > img, +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + display: block; + max-width: 100%; + height: auto; +} +.img-rounded { + border-radius: 6px; +} +.img-thumbnail { + padding: 4px; + line-height: 1.42857143; + background-color: #f8f5f0; + border: 1px solid #dfd7ca; + border-radius: 4px; + -webkit-transition: all 0.2s ease-in-out; + -o-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; + display: inline-block; + max-width: 100%; + height: auto; +} +.img-circle { + border-radius: 50%; +} +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #f8f5f0; +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} +.sr-only-focusable:active, +.sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto; +} +[role="button"] { + cursor: pointer; +} +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: inherit; + font-weight: 400; + line-height: 1.1; + color: inherit; +} +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small, +.h1 small, +.h2 small, +.h3 small, +.h4 small, +.h5 small, +.h6 small, +h1 .small, +h2 .small, +h3 .small, +h4 .small, +h5 .small, +h6 .small, +.h1 .small, +.h2 .small, +.h3 .small, +.h4 .small, +.h5 .small, +.h6 .small { + font-weight: normal; + line-height: 1; + color: #98978b; +} +h1, +.h1, +h2, +.h2, +h3, +.h3 { + margin-top: 20px; + margin-bottom: 10px; +} +h1 small, +.h1 small, +h2 small, +.h2 small, +h3 small, +.h3 small, +h1 .small, +.h1 .small, +h2 .small, +.h2 .small, +h3 .small, +.h3 .small { + font-size: 65%; +} +h4, +.h4, +h5, +.h5, +h6, +.h6 { + margin-top: 10px; + margin-bottom: 10px; +} +h4 small, +.h4 small, +h5 small, +.h5 small, +h6 small, +.h6 small, +h4 .small, +.h4 .small, +h5 .small, +.h5 .small, +h6 .small, +.h6 .small { + font-size: 75%; +} +h1, +.h1 { + font-size: 36px; +} +h2, +.h2 { + font-size: 30px; +} +h3, +.h3 { + font-size: 24px; +} +h4, +.h4 { + font-size: 18px; +} +h5, +.h5 { + font-size: 14px; +} +h6, +.h6 { + font-size: 12px; +} +p { + margin: 0 0 10px; +} +.lead { + margin-bottom: 20px; + font-size: 16px; + font-weight: 300; + line-height: 1.4; +} +@media (min-width: 768px) { + .lead { + font-size: 21px; + } +} +small, +.small { + font-size: 85%; +} +/*mark, +.mark { + background-color: #fcf8e3; + padding: .2em; +}*/ +.text-left { + text-align: left; +} +.text-right { + text-align: right; +} +.text-center { + text-align: center; +} +.text-justify { + text-align: justify; +} +.text-nowrap { + white-space: nowrap; +} +.text-lowercase { + text-transform: lowercase; +} +.text-uppercase { + text-transform: uppercase; +} +.text-capitalize { + text-transform: capitalize; +} +.text-muted { + color: #98978b; +} +.text-primary { + color: #325d88; +} +a.text-primary:hover, +a.text-primary:focus { + color: #244363; +} +.text-success { + color: #93c54b; +} +a.text-success:hover, +a.text-success:focus { + color: #79a736; +} +.text-info { + color: #29abe0; +} +a.text-info:hover, +a.text-info:focus { + color: #1b8dbb; +} +.text-warning { + color: #f47c3c; +} +a.text-warning:hover, +a.text-warning:focus { + color: #ef5c0e; +} +.text-danger { + color: #d9534f; +} +a.text-danger:hover, +a.text-danger:focus { + color: #c9302c; +} +.bg-primary { + color: #fff; + background-color: #325d88; +} +a.bg-primary:hover, +a.bg-primary:focus { + background-color: #244363; +} +.bg-success { + background-color: #dff0d8; +} +a.bg-success:hover, +a.bg-success:focus { + background-color: #c1e2b3; +} +.bg-info { + background-color: #d9edf7; +} +a.bg-info:hover, +a.bg-info:focus { + background-color: #afd9ee; +} +.bg-warning { + background-color: #fcf8e3; +} +a.bg-warning:hover, +a.bg-warning:focus { + background-color: #f7ecb5; +} +.bg-danger { + background-color: #f2dede; +} +a.bg-danger:hover, +a.bg-danger:focus { + background-color: #e4b9b9; +} +.page-header { + padding-bottom: 9px; + margin: 40px 0 20px; + border-bottom: 1px solid #f8f5f0; +} +ul, +ol { + margin-top: 0; + margin-bottom: 10px; +} +ul ul, +ol ul, +ul ol, +ol ol { + margin-bottom: 0; +} +.list-unstyled { + padding-left: 0; + list-style: none; +} +.list-inline { + padding-left: 0; + list-style: none; + margin-left: -5px; +} +.list-inline > li { + display: inline-block; + padding-left: 5px; + padding-right: 5px; +} +dl { + margin-top: 0; + margin-bottom: 20px; +} +dt, +dd { + line-height: 1.42857143; +} +dt { + font-weight: bold; +} +dd { + margin-left: 0; +} +@media (min-width: 768px) { + .dl-horizontal dt { + float: left; + width: 160px; + clear: left; + text-align: right; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .dl-horizontal dd { + margin-left: 180px; + } +} +abbr[title], +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #98978b; +} +.initialism { + font-size: 90%; + text-transform: uppercase; +} +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 17.5px; + border-left: 5px solid #dfd7ca; +} +blockquote p:last-child, +blockquote ul:last-child, +blockquote ol:last-child { + margin-bottom: 0; +} +blockquote footer, +blockquote small, +blockquote .small { + display: block; + font-size: 80%; + line-height: 1.42857143; + color: #3e3f3a; +} +blockquote footer:before, +blockquote small:before, +blockquote .small:before { + content: '\2014 \00A0'; +} +.blockquote-reverse, +blockquote.pull-right { + padding-right: 15px; + padding-left: 0; + border-right: 5px solid #dfd7ca; + border-left: 0; + text-align: right; +} +.blockquote-reverse footer:before, +blockquote.pull-right footer:before, +.blockquote-reverse small:before, +blockquote.pull-right small:before, +.blockquote-reverse .small:before, +blockquote.pull-right .small:before { + content: ''; +} +.blockquote-reverse footer:after, +blockquote.pull-right footer:after, +.blockquote-reverse small:after, +blockquote.pull-right small:after, +.blockquote-reverse .small:after, +blockquote.pull-right .small:after { + content: '\00A0 \2014'; +} +address { + margin-bottom: 20px; + font-style: normal; + line-height: 1.42857143; +} +code, +kbd, +pre, +samp { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; +} +code { + padding: 2px 4px; + font-size: 90%; + color: #c7254e; + background-color: #f9f2f4; + border-radius: 4px; +} +kbd { + padding: 2px 4px; + font-size: 90%; + color: #ffffff; + background-color: #333333; + border-radius: 3px; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25); +} +kbd kbd { + padding: 0; + font-size: 100%; + font-weight: bold; + -webkit-box-shadow: none; + box-shadow: none; +} +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.42857143; + word-break: break-all; + word-wrap: break-word; + color: #8e8c84; + background-color: #f5f5f5; + border: 1px solid #cccccc; + border-radius: 4px; +} +pre code { + padding: 0; + font-size: inherit; + color: inherit; + white-space: pre-wrap; + background-color: transparent; + border-radius: 0; +} +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} +.container { + margin-right: auto; + margin-left: auto; + padding-left: 15px; + padding-right: 15px; +} +@media (min-width: 768px) { + .container { + width: 750px; + } +} +@media (min-width: 992px) { + .container { + width: 970px; + } +} +@media (min-width: 1200px) { + .container { + width: 1170px; + } +} +.container-fluid { + margin-right: auto; + margin-left: auto; + padding-left: 15px; + padding-right: 15px; +} +.row { + margin-left: -15px; + margin-right: -15px; +} +.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { + position: relative; + min-height: 1px; + padding-left: 15px; + padding-right: 15px; +} +.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { + float: left; +} +.col-xs-12 { + width: 100%; +} +.col-xs-11 { + width: 91.66666667%; +} +.col-xs-10 { + width: 83.33333333%; +} +.col-xs-9 { + width: 75%; +} +.col-xs-8 { + width: 66.66666667%; +} +.col-xs-7 { + width: 58.33333333%; +} +.col-xs-6 { + width: 50%; +} +.col-xs-5 { + width: 41.66666667%; +} +.col-xs-4 { + width: 33.33333333%; +} +.col-xs-3 { + width: 25%; +} +.col-xs-2 { + width: 16.66666667%; +} +.col-xs-1 { + width: 8.33333333%; +} +.col-xs-pull-12 { + right: 100%; +} +.col-xs-pull-11 { + right: 91.66666667%; +} +.col-xs-pull-10 { + right: 83.33333333%; +} +.col-xs-pull-9 { + right: 75%; +} +.col-xs-pull-8 { + right: 66.66666667%; +} +.col-xs-pull-7 { + right: 58.33333333%; +} +.col-xs-pull-6 { + right: 50%; +} +.col-xs-pull-5 { + right: 41.66666667%; +} +.col-xs-pull-4 { + right: 33.33333333%; +} +.col-xs-pull-3 { + right: 25%; +} +.col-xs-pull-2 { + right: 16.66666667%; +} +.col-xs-pull-1 { + right: 8.33333333%; +} +.col-xs-pull-0 { + right: auto; +} +.col-xs-push-12 { + left: 100%; +} +.col-xs-push-11 { + left: 91.66666667%; +} +.col-xs-push-10 { + left: 83.33333333%; +} +.col-xs-push-9 { + left: 75%; +} +.col-xs-push-8 { + left: 66.66666667%; +} +.col-xs-push-7 { + left: 58.33333333%; +} +.col-xs-push-6 { + left: 50%; +} +.col-xs-push-5 { + left: 41.66666667%; +} +.col-xs-push-4 { + left: 33.33333333%; +} +.col-xs-push-3 { + left: 25%; +} +.col-xs-push-2 { + left: 16.66666667%; +} +.col-xs-push-1 { + left: 8.33333333%; +} +.col-xs-push-0 { + left: auto; +} +.col-xs-offset-12 { + margin-left: 100%; +} +.col-xs-offset-11 { + margin-left: 91.66666667%; +} +.col-xs-offset-10 { + margin-left: 83.33333333%; +} +.col-xs-offset-9 { + margin-left: 75%; +} +.col-xs-offset-8 { + margin-left: 66.66666667%; +} +.col-xs-offset-7 { + margin-left: 58.33333333%; +} +.col-xs-offset-6 { + margin-left: 50%; +} +.col-xs-offset-5 { + margin-left: 41.66666667%; +} +.col-xs-offset-4 { + margin-left: 33.33333333%; +} +.col-xs-offset-3 { + margin-left: 25%; +} +.col-xs-offset-2 { + margin-left: 16.66666667%; +} +.col-xs-offset-1 { + margin-left: 8.33333333%; +} +.col-xs-offset-0 { + margin-left: 0%; +} +@media (min-width: 768px) { + .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { + float: left; + } + .col-sm-12 { + width: 100%; + } + .col-sm-11 { + width: 91.66666667%; + } + .col-sm-10 { + width: 83.33333333%; + } + .col-sm-9 { + width: 75%; + } + .col-sm-8 { + width: 66.66666667%; + } + .col-sm-7 { + width: 58.33333333%; + } + .col-sm-6 { + width: 50%; + } + .col-sm-5 { + width: 41.66666667%; + } + .col-sm-4 { + width: 33.33333333%; + } + .col-sm-3 { + width: 25%; + } + .col-sm-2 { + width: 16.66666667%; + } + .col-sm-1 { + width: 8.33333333%; + } + .col-sm-pull-12 { + right: 100%; + } + .col-sm-pull-11 { + right: 91.66666667%; + } + .col-sm-pull-10 { + right: 83.33333333%; + } + .col-sm-pull-9 { + right: 75%; + } + .col-sm-pull-8 { + right: 66.66666667%; + } + .col-sm-pull-7 { + right: 58.33333333%; + } + .col-sm-pull-6 { + right: 50%; + } + .col-sm-pull-5 { + right: 41.66666667%; + } + .col-sm-pull-4 { + right: 33.33333333%; + } + .col-sm-pull-3 { + right: 25%; + } + .col-sm-pull-2 { + right: 16.66666667%; + } + .col-sm-pull-1 { + right: 8.33333333%; + } + .col-sm-pull-0 { + right: auto; + } + .col-sm-push-12 { + left: 100%; + } + .col-sm-push-11 { + left: 91.66666667%; + } + .col-sm-push-10 { + left: 83.33333333%; + } + .col-sm-push-9 { + left: 75%; + } + .col-sm-push-8 { + left: 66.66666667%; + } + .col-sm-push-7 { + left: 58.33333333%; + } + .col-sm-push-6 { + left: 50%; + } + .col-sm-push-5 { + left: 41.66666667%; + } + .col-sm-push-4 { + left: 33.33333333%; + } + .col-sm-push-3 { + left: 25%; + } + .col-sm-push-2 { + left: 16.66666667%; + } + .col-sm-push-1 { + left: 8.33333333%; + } + .col-sm-push-0 { + left: auto; + } + .col-sm-offset-12 { + margin-left: 100%; + } + .col-sm-offset-11 { + margin-left: 91.66666667%; + } + .col-sm-offset-10 { + margin-left: 83.33333333%; + } + .col-sm-offset-9 { + margin-left: 75%; + } + .col-sm-offset-8 { + margin-left: 66.66666667%; + } + .col-sm-offset-7 { + margin-left: 58.33333333%; + } + .col-sm-offset-6 { + margin-left: 50%; + } + .col-sm-offset-5 { + margin-left: 41.66666667%; + } + .col-sm-offset-4 { + margin-left: 33.33333333%; + } + .col-sm-offset-3 { + margin-left: 25%; + } + .col-sm-offset-2 { + margin-left: 16.66666667%; + } + .col-sm-offset-1 { + margin-left: 8.33333333%; + } + .col-sm-offset-0 { + margin-left: 0%; + } +} +@media (min-width: 992px) { + .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { + float: left; + } + .col-md-12 { + width: 100%; + } + .col-md-11 { + width: 91.66666667%; + } + .col-md-10 { + width: 83.33333333%; + } + .col-md-9 { + width: 75%; + } + .col-md-8 { + width: 66.66666667%; + } + .col-md-7 { + width: 58.33333333%; + } + .col-md-6 { + width: 50%; + } + .col-md-5 { + width: 41.66666667%; + } + .col-md-4 { + width: 33.33333333%; + } + .col-md-3 { + width: 25%; + } + .col-md-2 { + width: 16.66666667%; + } + .col-md-1 { + width: 8.33333333%; + } + .col-md-pull-12 { + right: 100%; + } + .col-md-pull-11 { + right: 91.66666667%; + } + .col-md-pull-10 { + right: 83.33333333%; + } + .col-md-pull-9 { + right: 75%; + } + .col-md-pull-8 { + right: 66.66666667%; + } + .col-md-pull-7 { + right: 58.33333333%; + } + .col-md-pull-6 { + right: 50%; + } + .col-md-pull-5 { + right: 41.66666667%; + } + .col-md-pull-4 { + right: 33.33333333%; + } + .col-md-pull-3 { + right: 25%; + } + .col-md-pull-2 { + right: 16.66666667%; + } + .col-md-pull-1 { + right: 8.33333333%; + } + .col-md-pull-0 { + right: auto; + } + .col-md-push-12 { + left: 100%; + } + .col-md-push-11 { + left: 91.66666667%; + } + .col-md-push-10 { + left: 83.33333333%; + } + .col-md-push-9 { + left: 75%; + } + .col-md-push-8 { + left: 66.66666667%; + } + .col-md-push-7 { + left: 58.33333333%; + } + .col-md-push-6 { + left: 50%; + } + .col-md-push-5 { + left: 41.66666667%; + } + .col-md-push-4 { + left: 33.33333333%; + } + .col-md-push-3 { + left: 25%; + } + .col-md-push-2 { + left: 16.66666667%; + } + .col-md-push-1 { + left: 8.33333333%; + } + .col-md-push-0 { + left: auto; + } + .col-md-offset-12 { + margin-left: 100%; + } + .col-md-offset-11 { + margin-left: 91.66666667%; + } + .col-md-offset-10 { + margin-left: 83.33333333%; + } + .col-md-offset-9 { + margin-left: 75%; + } + .col-md-offset-8 { + margin-left: 66.66666667%; + } + .col-md-offset-7 { + margin-left: 58.33333333%; + } + .col-md-offset-6 { + margin-left: 50%; + } + .col-md-offset-5 { + margin-left: 41.66666667%; + } + .col-md-offset-4 { + margin-left: 33.33333333%; + } + .col-md-offset-3 { + margin-left: 25%; + } + .col-md-offset-2 { + margin-left: 16.66666667%; + } + .col-md-offset-1 { + margin-left: 8.33333333%; + } + .col-md-offset-0 { + margin-left: 0%; + } +} +@media (min-width: 1200px) { + .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { + float: left; + } + .col-lg-12 { + width: 100%; + } + .col-lg-11 { + width: 91.66666667%; + } + .col-lg-10 { + width: 83.33333333%; + } + .col-lg-9 { + width: 75%; + } + .col-lg-8 { + width: 66.66666667%; + } + .col-lg-7 { + width: 58.33333333%; + } + .col-lg-6 { + width: 50%; + } + .col-lg-5 { + width: 41.66666667%; + } + .col-lg-4 { + width: 33.33333333%; + } + .col-lg-3 { + width: 25%; + } + .col-lg-2 { + width: 16.66666667%; + } + .col-lg-1 { + width: 8.33333333%; + } + .col-lg-pull-12 { + right: 100%; + } + .col-lg-pull-11 { + right: 91.66666667%; + } + .col-lg-pull-10 { + right: 83.33333333%; + } + .col-lg-pull-9 { + right: 75%; + } + .col-lg-pull-8 { + right: 66.66666667%; + } + .col-lg-pull-7 { + right: 58.33333333%; + } + .col-lg-pull-6 { + right: 50%; + } + .col-lg-pull-5 { + right: 41.66666667%; + } + .col-lg-pull-4 { + right: 33.33333333%; + } + .col-lg-pull-3 { + right: 25%; + } + .col-lg-pull-2 { + right: 16.66666667%; + } + .col-lg-pull-1 { + right: 8.33333333%; + } + .col-lg-pull-0 { + right: auto; + } + .col-lg-push-12 { + left: 100%; + } + .col-lg-push-11 { + left: 91.66666667%; + } + .col-lg-push-10 { + left: 83.33333333%; + } + .col-lg-push-9 { + left: 75%; + } + .col-lg-push-8 { + left: 66.66666667%; + } + .col-lg-push-7 { + left: 58.33333333%; + } + .col-lg-push-6 { + left: 50%; + } + .col-lg-push-5 { + left: 41.66666667%; + } + .col-lg-push-4 { + left: 33.33333333%; + } + .col-lg-push-3 { + left: 25%; + } + .col-lg-push-2 { + left: 16.66666667%; + } + .col-lg-push-1 { + left: 8.33333333%; + } + .col-lg-push-0 { + left: auto; + } + .col-lg-offset-12 { + margin-left: 100%; + } + .col-lg-offset-11 { + margin-left: 91.66666667%; + } + .col-lg-offset-10 { + margin-left: 83.33333333%; + } + .col-lg-offset-9 { + margin-left: 75%; + } + .col-lg-offset-8 { + margin-left: 66.66666667%; + } + .col-lg-offset-7 { + margin-left: 58.33333333%; + } + .col-lg-offset-6 { + margin-left: 50%; + } + .col-lg-offset-5 { + margin-left: 41.66666667%; + } + .col-lg-offset-4 { + margin-left: 33.33333333%; + } + .col-lg-offset-3 { + margin-left: 25%; + } + .col-lg-offset-2 { + margin-left: 16.66666667%; + } + .col-lg-offset-1 { + margin-left: 8.33333333%; + } + .col-lg-offset-0 { + margin-left: 0%; + } +} +table { + background-color: transparent; +} +caption { + padding-top: 8px; + padding-bottom: 8px; + color: #98978b; + text-align: left; +} +th { + text-align: left; +} +.table { + width: 100%; + max-width: 100%; + margin-bottom: 20px; +} +.table > thead > tr > th, +.table > tbody > tr > th, +.table > tfoot > tr > th, +.table > thead > tr > td, +.table > tbody > tr > td, +.table > tfoot > tr > td { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid #dfd7ca; +} +.table > thead > tr > th { + vertical-align: bottom; + border-bottom: 2px solid #dfd7ca; +} +.table > caption + thead > tr:first-child > th, +.table > colgroup + thead > tr:first-child > th, +.table > thead:first-child > tr:first-child > th, +.table > caption + thead > tr:first-child > td, +.table > colgroup + thead > tr:first-child > td, +.table > thead:first-child > tr:first-child > td { + border-top: 0; +} +.table > tbody + tbody { + border-top: 2px solid #dfd7ca; +} +.table .table { + background-color: #ffffff; +} +.table-condensed > thead > tr > th, +.table-condensed > tbody > tr > th, +.table-condensed > tfoot > tr > th, +.table-condensed > thead > tr > td, +.table-condensed > tbody > tr > td, +.table-condensed > tfoot > tr > td { + padding: 5px; +} +.table-bordered { + border: 1px solid #dfd7ca; +} +.table-bordered > thead > tr > th, +.table-bordered > tbody > tr > th, +.table-bordered > tfoot > tr > th, +.table-bordered > thead > tr > td, +.table-bordered > tbody > tr > td, +.table-bordered > tfoot > tr > td { + border: 1px solid #dfd7ca; +} +.table-bordered > thead > tr > th, +.table-bordered > thead > tr > td { + border-bottom-width: 2px; +} +.table-striped > tbody > tr:nth-of-type(odd) { + background-color: #f8f5f0; +} +.table-hover > tbody > tr:hover { + background-color: #f8f5f0; +} +table col[class*="col-"] { + position: static; + float: none; + display: table-column; +} +table td[class*="col-"], +table th[class*="col-"] { + position: static; + float: none; + display: table-cell; +} +.table > thead > tr > td.active, +.table > tbody > tr > td.active, +.table > tfoot > tr > td.active, +.table > thead > tr > th.active, +.table > tbody > tr > th.active, +.table > tfoot > tr > th.active, +.table > thead > tr.active > td, +.table > tbody > tr.active > td, +.table > tfoot > tr.active > td, +.table > thead > tr.active > th, +.table > tbody > tr.active > th, +.table > tfoot > tr.active > th { + background-color: #f8f5f0; +} +.table-hover > tbody > tr > td.active:hover, +.table-hover > tbody > tr > th.active:hover, +.table-hover > tbody > tr.active:hover > td, +.table-hover > tbody > tr:hover > .active, +.table-hover > tbody > tr.active:hover > th { + background-color: #f0e9df; +} +.table > thead > tr > td.success, +.table > tbody > tr > td.success, +.table > tfoot > tr > td.success, +.table > thead > tr > th.success, +.table > tbody > tr > th.success, +.table > tfoot > tr > th.success, +.table > thead > tr.success > td, +.table > tbody > tr.success > td, +.table > tfoot > tr.success > td, +.table > thead > tr.success > th, +.table > tbody > tr.success > th, +.table > tfoot > tr.success > th { + background-color: #dff0d8; +} +.table-hover > tbody > tr > td.success:hover, +.table-hover > tbody > tr > th.success:hover, +.table-hover > tbody > tr.success:hover > td, +.table-hover > tbody > tr:hover > .success, +.table-hover > tbody > tr.success:hover > th { + background-color: #d0e9c6; +} +.table > thead > tr > td.info, +.table > tbody > tr > td.info, +.table > tfoot > tr > td.info, +.table > thead > tr > th.info, +.table > tbody > tr > th.info, +.table > tfoot > tr > th.info, +.table > thead > tr.info > td, +.table > tbody > tr.info > td, +.table > tfoot > tr.info > td, +.table > thead > tr.info > th, +.table > tbody > tr.info > th, +.table > tfoot > tr.info > th { + background-color: #d9edf7; +} +.table-hover > tbody > tr > td.info:hover, +.table-hover > tbody > tr > th.info:hover, +.table-hover > tbody > tr.info:hover > td, +.table-hover > tbody > tr:hover > .info, +.table-hover > tbody > tr.info:hover > th { + background-color: #c4e3f3; +} +.table > thead > tr > td.warning, +.table > tbody > tr > td.warning, +.table > tfoot > tr > td.warning, +.table > thead > tr > th.warning, +.table > tbody > tr > th.warning, +.table > tfoot > tr > th.warning, +.table > thead > tr.warning > td, +.table > tbody > tr.warning > td, +.table > tfoot > tr.warning > td, +.table > thead > tr.warning > th, +.table > tbody > tr.warning > th, +.table > tfoot > tr.warning > th { + background-color: #fcf8e3; +} +.table-hover > tbody > tr > td.warning:hover, +.table-hover > tbody > tr > th.warning:hover, +.table-hover > tbody > tr.warning:hover > td, +.table-hover > tbody > tr:hover > .warning, +.table-hover > tbody > tr.warning:hover > th { + background-color: #faf2cc; +} +.table > thead > tr > td.danger, +.table > tbody > tr > td.danger, +.table > tfoot > tr > td.danger, +.table > thead > tr > th.danger, +.table > tbody > tr > th.danger, +.table > tfoot > tr > th.danger, +.table > thead > tr.danger > td, +.table > tbody > tr.danger > td, +.table > tfoot > tr.danger > td, +.table > thead > tr.danger > th, +.table > tbody > tr.danger > th, +.table > tfoot > tr.danger > th { + background-color: #f2dede; +} +.table-hover > tbody > tr > td.danger:hover, +.table-hover > tbody > tr > th.danger:hover, +.table-hover > tbody > tr.danger:hover > td, +.table-hover > tbody > tr:hover > .danger, +.table-hover > tbody > tr.danger:hover > th { + background-color: #ebcccc; +} +.table-responsive { + overflow-x: auto; + min-height: 0.01%; +} +@media screen and (max-width: 767px) { + .table-responsive { + width: 100%; + margin-bottom: 15px; + overflow-y: hidden; + -ms-overflow-style: -ms-autohiding-scrollbar; + border: 1px solid #dfd7ca; + } + .table-responsive > .table { + margin-bottom: 0; + } + .table-responsive > .table > thead > tr > th, + .table-responsive > .table > tbody > tr > th, + .table-responsive > .table > tfoot > tr > th, + .table-responsive > .table > thead > tr > td, + .table-responsive > .table > tbody > tr > td, + .table-responsive > .table > tfoot > tr > td { + white-space: nowrap; + } + .table-responsive > .table-bordered { + border: 0; + } + .table-responsive > .table-bordered > thead > tr > th:first-child, + .table-responsive > .table-bordered > tbody > tr > th:first-child, + .table-responsive > .table-bordered > tfoot > tr > th:first-child, + .table-responsive > .table-bordered > thead > tr > td:first-child, + .table-responsive > .table-bordered > tbody > tr > td:first-child, + .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; + } + .table-responsive > .table-bordered > thead > tr > th:last-child, + .table-responsive > .table-bordered > tbody > tr > th:last-child, + .table-responsive > .table-bordered > tfoot > tr > th:last-child, + .table-responsive > .table-bordered > thead > tr > td:last-child, + .table-responsive > .table-bordered > tbody > tr > td:last-child, + .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; + } + .table-responsive > .table-bordered > tbody > tr:last-child > th, + .table-responsive > .table-bordered > tfoot > tr:last-child > th, + .table-responsive > .table-bordered > tbody > tr:last-child > td, + .table-responsive > .table-bordered > tfoot > tr:last-child > td { + border-bottom: 0; + } +} +fieldset { + padding: 0; + margin: 0; + border: 0; + min-width: 0; +} +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: inherit; + color: inherit; + border: 0; + border-bottom: 1px solid transparent; +} +label { + display: inline-block; + max-width: 100%; + margin-bottom: 5px; + font-weight: bold; +} +input[type="search"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; + line-height: normal; +} +input[type="file"] { + display: block; +} +input[type="range"] { + display: block; + width: 100%; +} +select[multiple], +select[size] { + height: auto; +} +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +output { + display: block; + padding-top: 13px; + font-size: 14px; + line-height: 1.42857143; + color: #3e3f3a; +} +.form-control { + display: block; + width: 100%; + height: 46px; + padding: 12px 16px; + font-size: 14px; + line-height: 1.42857143; + color: #3e3f3a; + background-color: #ffffff; + background-image: none; + border: 1px solid #dfd7ca; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; +} +/* .form-control:focus { + border-color: transparent; + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(0, 0, 0, 0.6); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(0, 0, 0, 0.6); +} */ +.form-control::-moz-placeholder { + color: #dfd7ca; + opacity: 1; +} +.form-control:-ms-input-placeholder { + color: #dfd7ca; +} +.form-control::-webkit-input-placeholder { + color: #dfd7ca; +} +.form-control::-ms-expand { + border: 0; + background-color: transparent; +} +.form-control[disabled], +.form-control[readonly], +fieldset[disabled] .form-control { + background-color: #f8f5f0; + opacity: 1; +} +.form-control[disabled], +fieldset[disabled] .form-control { + cursor: not-allowed; +} +textarea.form-control { + height: auto; +} +input[type="search"] { + -webkit-appearance: none; +} +@media screen and (-webkit-min-device-pixel-ratio: 0) { + input[type="date"].form-control, + input[type="time"].form-control, + input[type="datetime-local"].form-control, + input[type="month"].form-control { + line-height: 46px; + } + input[type="date"].input-sm, + input[type="time"].input-sm, + input[type="datetime-local"].input-sm, + input[type="month"].input-sm, + .input-group-sm input[type="date"], + .input-group-sm input[type="time"], + .input-group-sm input[type="datetime-local"], + .input-group-sm input[type="month"] { + line-height: 30px; + } + input[type="date"].input-lg, + input[type="time"].input-lg, + input[type="datetime-local"].input-lg, + input[type="month"].input-lg, + .input-group-lg input[type="date"], + .input-group-lg input[type="time"], + .input-group-lg input[type="datetime-local"], + .input-group-lg input[type="month"] { + line-height: 66px; + } +} +.form-group { + margin-bottom: 15px; +} +.radio, +.checkbox { + position: relative; + display: block; + margin-top: 10px; + margin-bottom: 10px; +} +.radio label, +.checkbox label { + min-height: 20px; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + cursor: pointer; +} +.radio input[type="radio"], +.radio-inline input[type="radio"], +.checkbox input[type="checkbox"], +.checkbox-inline input[type="checkbox"] { + position: absolute; + margin-left: -20px; + margin-top: 4px \9; +} +.radio + .radio, +.checkbox + .checkbox { + margin-top: -5px; +} +.radio-inline, +.checkbox-inline { + position: relative; + display: inline-block; + padding-left: 20px; + margin-bottom: 0; + vertical-align: middle; + font-weight: normal; + cursor: pointer; +} +.radio-inline + .radio-inline, +.checkbox-inline + .checkbox-inline { + margin-top: 0; + margin-left: 10px; +} +input[type="radio"][disabled], +input[type="checkbox"][disabled], +input[type="radio"].disabled, +input[type="checkbox"].disabled, +fieldset[disabled] input[type="radio"], +fieldset[disabled] input[type="checkbox"] { + cursor: not-allowed; +} +.radio-inline.disabled, +.checkbox-inline.disabled, +fieldset[disabled] .radio-inline, +fieldset[disabled] .checkbox-inline { + cursor: not-allowed; +} +.radio.disabled label, +.checkbox.disabled label, +fieldset[disabled] .radio label, +fieldset[disabled] .checkbox label { + cursor: not-allowed; +} +.form-control-static { + padding-top: 13px; + padding-bottom: 13px; + margin-bottom: 0; + min-height: 34px; +} +.form-control-static.input-lg, +.form-control-static.input-sm { + padding-left: 0; + padding-right: 0; +} +.input-sm { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-sm { + height: 30px; + line-height: 30px; +} +textarea.input-sm, +select[multiple].input-sm { + height: auto; +} +.form-group-sm .form-control { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.form-group-sm select.form-control { + height: 30px; + line-height: 30px; +} +.form-group-sm textarea.form-control, +.form-group-sm select[multiple].form-control { + height: auto; +} +.form-group-sm .form-control-static { + height: 30px; + min-height: 32px; + padding: 6px 10px; + font-size: 12px; + line-height: 1.5; +} +.input-lg { + height: 66px; + padding: 20px 30px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +select.input-lg { + height: 66px; + line-height: 66px; +} +textarea.input-lg, +select[multiple].input-lg { + height: auto; +} +.form-group-lg .form-control { + height: 66px; + padding: 20px 30px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +.form-group-lg select.form-control { + height: 66px; + line-height: 66px; +} +.form-group-lg textarea.form-control, +.form-group-lg select[multiple].form-control { + height: auto; +} +.form-group-lg .form-control-static { + height: 66px; + min-height: 38px; + padding: 21px 30px; + font-size: 18px; + line-height: 1.3333333; +} +.has-feedback { + position: relative; +} +.has-feedback .form-control { + padding-right: 57.5px; +} +.form-control-feedback { + position: absolute; + top: 0; + right: 0; + z-index: 2; + display: block; + width: 46px; + height: 46px; + line-height: 46px; + text-align: center; + pointer-events: none; +} +.input-lg + .form-control-feedback, +.input-group-lg + .form-control-feedback, +.form-group-lg .form-control + .form-control-feedback { + width: 66px; + height: 66px; + line-height: 66px; +} +.input-sm + .form-control-feedback, +.input-group-sm + .form-control-feedback, +.form-group-sm .form-control + .form-control-feedback { + width: 30px; + height: 30px; + line-height: 30px; +} +.has-success .help-block, +.has-success .control-label, +.has-success .radio, +.has-success .checkbox, +.has-success .radio-inline, +.has-success .checkbox-inline, +.has-success.radio label, +.has-success.checkbox label, +.has-success.radio-inline label, +.has-success.checkbox-inline label { + color: #93c54b; +} +.has-success .form-control { + border-color: #93c54b; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.has-success .form-control:focus { + border-color: #79a736; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c1de98; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c1de98; +} +.has-success .input-group-addon { + color: #93c54b; + border-color: #93c54b; + background-color: #dff0d8; +} +.has-success .form-control-feedback { + color: #93c54b; +} +.has-warning .help-block, +.has-warning .control-label, +.has-warning .radio, +.has-warning .checkbox, +.has-warning .radio-inline, +.has-warning .checkbox-inline, +.has-warning.radio label, +.has-warning.checkbox label, +.has-warning.radio-inline label, +.has-warning.checkbox-inline label { + color: #f47c3c; +} +.has-warning .form-control { + border-color: #f47c3c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.has-warning .form-control:focus { + border-color: #ef5c0e; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #f9bd9d; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #f9bd9d; +} +.has-warning .input-group-addon { + color: #f47c3c; + border-color: #f47c3c; + background-color: #fcf8e3; +} +.has-warning .form-control-feedback { + color: #f47c3c; +} +.has-error .help-block, +.has-error .control-label, +.has-error .radio, +.has-error .checkbox, +.has-error .radio-inline, +.has-error .checkbox-inline, +.has-error.radio label, +.has-error.checkbox label, +.has-error.radio-inline label, +.has-error.checkbox-inline label { + color: #d9534f; +} +.has-error .form-control { + border-color: #d9534f; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.has-error .form-control:focus { + border-color: #c9302c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #eba5a3; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #eba5a3; +} +.has-error .input-group-addon { + color: #d9534f; + border-color: #d9534f; + background-color: #f2dede; +} +.has-error .form-control-feedback { + color: #d9534f; +} +.has-feedback label ~ .form-control-feedback { + top: 25px; +} +.has-feedback label.sr-only ~ .form-control-feedback { + top: 0; +} +.help-block { + display: block; + margin-top: 5px; + margin-bottom: 10px; + color: #7f8177; +} +@media (min-width: 768px) { + .form-inline .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .form-inline .form-control-static { + display: inline-block; + } + .form-inline .input-group { + display: inline-table; + vertical-align: middle; + } + .form-inline .input-group .input-group-addon, + .form-inline .input-group .input-group-btn, + .form-inline .input-group .form-control { + width: auto; + } + .form-inline .input-group > .form-control { + width: 100%; + } + .form-inline .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio, + .form-inline .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio label, + .form-inline .checkbox label { + padding-left: 0; + } + .form-inline .radio input[type="radio"], + .form-inline .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + .form-inline .has-feedback .form-control-feedback { + top: 0; + } +} +.form-horizontal .radio, +.form-horizontal .checkbox, +.form-horizontal .radio-inline, +.form-horizontal .checkbox-inline { + margin-top: 0; + margin-bottom: 0; + padding-top: 13px; +} +.form-horizontal .radio, +.form-horizontal .checkbox { + min-height: 33px; +} +.form-horizontal .form-group { + margin-left: -15px; + margin-right: -15px; +} +@media (min-width: 768px) { + .form-horizontal .control-label { + text-align: right; + margin-bottom: 0; + padding-top: 13px; + } +} +.form-horizontal .has-feedback .form-control-feedback { + right: 15px; +} +@media (min-width: 768px) { + .form-horizontal .form-group-lg .control-label { + padding-top: 21px; + font-size: 18px; + } +} +@media (min-width: 768px) { + .form-horizontal .form-group-sm .control-label { + padding-top: 6px; + font-size: 12px; + } +} +.btn { + display: inline-block; + margin-bottom: 0; + font-weight: normal; + text-align: center; + vertical-align: middle; + -ms-touch-action: manipulation; + touch-action: manipulation; + cursor: pointer; + background-image: none; + border: 1px solid transparent; + white-space: nowrap; + padding: 12px 16px; + font-size: 14px; + line-height: 1.42857143; + border-radius: 4px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.btn:focus, +.btn:active:focus, +.btn.active:focus, +.btn.focus, +.btn:active.focus, +.btn.active.focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.btn:hover, +.btn:focus, +.btn.focus { + color: #ffffff; + text-decoration: none; +} +.btn:active, +.btn.active { + outline: 0; + background-image: none; + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); +} +.btn.disabled, +.btn[disabled], +fieldset[disabled] .btn { + cursor: not-allowed; + opacity: 0.65; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + box-shadow: none; +} +a.btn.disabled, +fieldset[disabled] a.btn { + pointer-events: none; +} +.btn-default { + color: #ffffff; + background-color: #3e3f3a; + border-color: transparent; +} +.btn-default:focus, +.btn-default.focus { + color: #ffffff; + background-color: #242422; + border-color: rgba(0, 0, 0, 0); +} +.btn-default:hover { + color: #ffffff; + background-color: #242422; + border-color: rgba(0, 0, 0, 0); +} +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + color: #ffffff; + background-color: #242422; + border-color: rgba(0, 0, 0, 0); +} +.btn-default:active:hover, +.btn-default.active:hover, +.open > .dropdown-toggle.btn-default:hover, +.btn-default:active:focus, +.btn-default.active:focus, +.open > .dropdown-toggle.btn-default:focus, +.btn-default:active.focus, +.btn-default.active.focus, +.open > .dropdown-toggle.btn-default.focus { + color: #ffffff; + background-color: #121210; + border-color: rgba(0, 0, 0, 0); +} +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + background-image: none; +} +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled.focus, +.btn-default[disabled].focus, +fieldset[disabled] .btn-default.focus { + background-color: #3e3f3a; + border-color: transparent; +} +.btn-default .badge { + color: #3e3f3a; + background-color: #ffffff; +} +.btn-primary { + color: #ffffff; + background-color: #325d88; + border-color: transparent; +} +.btn-primary:focus, +.btn-primary.focus { + color: #ffffff; + background-color: #244363; + border-color: rgba(0, 0, 0, 0); +} +.btn-primary:hover { + color: #ffffff; + background-color: #244363; + border-color: rgba(0, 0, 0, 0); +} +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + color: #ffffff; + background-color: #244363; + border-color: rgba(0, 0, 0, 0); +} +.btn-primary:active:hover, +.btn-primary.active:hover, +.open > .dropdown-toggle.btn-primary:hover, +.btn-primary:active:focus, +.btn-primary.active:focus, +.open > .dropdown-toggle.btn-primary:focus, +.btn-primary:active.focus, +.btn-primary.active.focus, +.open > .dropdown-toggle.btn-primary.focus { + color: #ffffff; + background-color: #1b3249; + border-color: rgba(0, 0, 0, 0); +} +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + background-image: none; +} +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled.focus, +.btn-primary[disabled].focus, +fieldset[disabled] .btn-primary.focus { + background-color: #325d88; + border-color: transparent; +} +.btn-primary .badge { + color: #325d88; + background-color: #ffffff; +} +.btn-success { + color: #ffffff; + background-color: #93c54b; + border-color: transparent; +} +.btn-success:focus, +.btn-success.focus { + color: #ffffff; + background-color: #79a736; + border-color: rgba(0, 0, 0, 0); +} +.btn-success:hover { + color: #ffffff; + background-color: #79a736; + border-color: rgba(0, 0, 0, 0); +} +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + color: #ffffff; + background-color: #79a736; + border-color: rgba(0, 0, 0, 0); +} +.btn-success:active:hover, +.btn-success.active:hover, +.open > .dropdown-toggle.btn-success:hover, +.btn-success:active:focus, +.btn-success.active:focus, +.open > .dropdown-toggle.btn-success:focus, +.btn-success:active.focus, +.btn-success.active.focus, +.open > .dropdown-toggle.btn-success.focus { + color: #ffffff; + background-color: #658c2d; + border-color: rgba(0, 0, 0, 0); +} +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + background-image: none; +} +.btn-success.disabled:hover, +.btn-success[disabled]:hover, +fieldset[disabled] .btn-success:hover, +.btn-success.disabled:focus, +.btn-success[disabled]:focus, +fieldset[disabled] .btn-success:focus, +.btn-success.disabled.focus, +.btn-success[disabled].focus, +fieldset[disabled] .btn-success.focus { + background-color: #93c54b; + border-color: transparent; +} +.btn-success .badge { + color: #93c54b; + background-color: #ffffff; +} +.btn-info { + color: #ffffff; + background-color: #29abe0; + border-color: transparent; +} +.btn-info:focus, +.btn-info.focus { + color: #ffffff; + background-color: #1b8dbb; + border-color: rgba(0, 0, 0, 0); +} +.btn-info:hover { + color: #ffffff; + background-color: #1b8dbb; + border-color: rgba(0, 0, 0, 0); +} +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + color: #ffffff; + background-color: #1b8dbb; + border-color: rgba(0, 0, 0, 0); +} +.btn-info:active:hover, +.btn-info.active:hover, +.open > .dropdown-toggle.btn-info:hover, +.btn-info:active:focus, +.btn-info.active:focus, +.open > .dropdown-toggle.btn-info:focus, +.btn-info:active.focus, +.btn-info.active.focus, +.open > .dropdown-toggle.btn-info.focus { + color: #ffffff; + background-color: #17759c; + border-color: rgba(0, 0, 0, 0); +} +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + background-image: none; +} +.btn-info.disabled:hover, +.btn-info[disabled]:hover, +fieldset[disabled] .btn-info:hover, +.btn-info.disabled:focus, +.btn-info[disabled]:focus, +fieldset[disabled] .btn-info:focus, +.btn-info.disabled.focus, +.btn-info[disabled].focus, +fieldset[disabled] .btn-info.focus { + background-color: #29abe0; + border-color: transparent; +} +.btn-info .badge { + color: #29abe0; + background-color: #ffffff; +} +.btn-warning { + color: #ffffff; + background-color: #f47c3c; + border-color: transparent; +} +.btn-warning:focus, +.btn-warning.focus { + color: #ffffff; + background-color: #ef5c0e; + border-color: rgba(0, 0, 0, 0); +} +.btn-warning:hover { + color: #ffffff; + background-color: #ef5c0e; + border-color: rgba(0, 0, 0, 0); +} +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + color: #ffffff; + background-color: #ef5c0e; + border-color: rgba(0, 0, 0, 0); +} +.btn-warning:active:hover, +.btn-warning.active:hover, +.open > .dropdown-toggle.btn-warning:hover, +.btn-warning:active:focus, +.btn-warning.active:focus, +.open > .dropdown-toggle.btn-warning:focus, +.btn-warning:active.focus, +.btn-warning.active.focus, +.open > .dropdown-toggle.btn-warning.focus { + color: #ffffff; + background-color: #ce4f0c; + border-color: rgba(0, 0, 0, 0); +} +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + background-image: none; +} +.btn-warning.disabled:hover, +.btn-warning[disabled]:hover, +fieldset[disabled] .btn-warning:hover, +.btn-warning.disabled:focus, +.btn-warning[disabled]:focus, +fieldset[disabled] .btn-warning:focus, +.btn-warning.disabled.focus, +.btn-warning[disabled].focus, +fieldset[disabled] .btn-warning.focus { + background-color: #f47c3c; + border-color: transparent; +} +.btn-warning .badge { + color: #f47c3c; + background-color: #ffffff; +} +.btn-danger { + color: #ffffff; + background-color: #d9534f; + border-color: transparent; +} +.btn-danger:focus, +.btn-danger.focus { + color: #ffffff; + background-color: #c9302c; + border-color: rgba(0, 0, 0, 0); +} +.btn-danger:hover { + color: #ffffff; + background-color: #c9302c; + border-color: rgba(0, 0, 0, 0); +} +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + color: #ffffff; + background-color: #c9302c; + border-color: rgba(0, 0, 0, 0); +} +.btn-danger:active:hover, +.btn-danger.active:hover, +.open > .dropdown-toggle.btn-danger:hover, +.btn-danger:active:focus, +.btn-danger.active:focus, +.open > .dropdown-toggle.btn-danger:focus, +.btn-danger:active.focus, +.btn-danger.active.focus, +.open > .dropdown-toggle.btn-danger.focus { + color: #ffffff; + background-color: #ac2925; + border-color: rgba(0, 0, 0, 0); +} +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + background-image: none; +} +.btn-danger.disabled:hover, +.btn-danger[disabled]:hover, +fieldset[disabled] .btn-danger:hover, +.btn-danger.disabled:focus, +.btn-danger[disabled]:focus, +fieldset[disabled] .btn-danger:focus, +.btn-danger.disabled.focus, +.btn-danger[disabled].focus, +fieldset[disabled] .btn-danger.focus { + background-color: #d9534f; + border-color: transparent; +} +.btn-danger .badge { + color: #d9534f; + background-color: #ffffff; +} +.btn-link { + color: #93c54b; + font-weight: normal; + border-radius: 0; +} +.btn-link, +.btn-link:active, +.btn-link.active, +.btn-link[disabled], +fieldset[disabled] .btn-link { + background-color: transparent; + -webkit-box-shadow: none; + box-shadow: none; +} +.btn-link, +.btn-link:hover, +.btn-link:focus, +.btn-link:active { + border-color: transparent; +} +.btn-link:hover, +.btn-link:focus { + color: #79a736; + text-decoration: underline; + background-color: transparent; +} +.btn-link[disabled]:hover, +fieldset[disabled] .btn-link:hover, +.btn-link[disabled]:focus, +fieldset[disabled] .btn-link:focus { + color: #dfd7ca; + text-decoration: none; +} +.btn-lg, +.btn-group-lg > .btn { + padding: 20px 30px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +.btn-sm, +.btn-group-sm > .btn { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-xs, +.btn-group-xs > .btn { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-block { + display: block; + width: 100%; +} +.btn-block + .btn-block { + margin-top: 5px; +} +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} +.fade { + opacity: 0; + -webkit-transition: opacity 0.15s linear; + -o-transition: opacity 0.15s linear; + transition: opacity 0.15s linear; +} +.fade.in { + opacity: 1; +} +.collapse { + display: none; +} +.collapse.in { + display: block; +} +tr.collapse.in { + display: table-row; +} +tbody.collapse.in { + display: table-row-group; +} +.collapsing { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition-property: height, visibility; + -o-transition-property: height, visibility; + transition-property: height, visibility; + -webkit-transition-duration: 0.35s; + -o-transition-duration: 0.35s; + transition-duration: 0.35s; + -webkit-transition-timing-function: ease; + -o-transition-timing-function: ease; + transition-timing-function: ease; +} +.caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 2px; + vertical-align: middle; + border-top: 4px dashed; + border-top: 4px solid \9; + border-right: 4px solid transparent; + border-left: 4px solid transparent; +} +.dropup, +.dropdown { + position: relative; +} +.dropdown-toggle:focus { + outline: 0; +} +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + list-style: none; + font-size: 14px; + text-align: left; + background-color: #ffffff; + border: 1px solid #dfd7ca; + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + -webkit-background-clip: padding-box; + background-clip: padding-box; +} +.dropdown-menu.pull-right { + right: 0; + left: auto; +} +.dropdown-menu .divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #f8f5f0; +} +.dropdown-menu > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 1.42857143; + color: #98978b; + white-space: nowrap; +} +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + text-decoration: none; + color: #98978b; + background-color: #f8f5f0; +} +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + color: #98978b; + text-decoration: none; + outline: 0; + background-color: #f8f5f0; +} +.dropdown-menu > .disabled > a, +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + color: #dfd7ca; +} +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + text-decoration: none; + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + cursor: not-allowed; +} +.open > .dropdown-menu { + display: block; +} +.open > a { + outline: 0; +} +.dropdown-menu-right { + left: auto; + right: 0; +} +.dropdown-menu-left { + left: 0; + right: auto; +} +.dropdown-header { + display: block; + padding: 3px 20px; + font-size: 12px; + line-height: 1.42857143; + color: #dfd7ca; + white-space: nowrap; +} +.dropdown-backdrop { + position: fixed; + left: 0; + right: 0; + bottom: 0; + top: 0; + z-index: 990; +} +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + border-top: 0; + border-bottom: 4px dashed; + border-bottom: 4px solid \9; + content: ""; +} +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 2px; +} +@media (min-width: 768px) { + .navbar-right .dropdown-menu { + left: auto; + right: 0; + } + .navbar-right .dropdown-menu-left { + left: 0; + right: auto; + } +} +.btn-group, +.btn-group-vertical { + position: relative; + display: inline-block; + vertical-align: middle; +} +.btn-group > .btn, +.btn-group-vertical > .btn { + position: relative; + float: left; +} +.btn-group > .btn:hover, +.btn-group-vertical > .btn:hover, +.btn-group > .btn:focus, +.btn-group-vertical > .btn:focus, +.btn-group > .btn:active, +.btn-group-vertical > .btn:active, +.btn-group > .btn.active, +.btn-group-vertical > .btn.active { + z-index: 2; +} +.btn-group .btn + .btn, +.btn-group .btn + .btn-group, +.btn-group .btn-group + .btn, +.btn-group .btn-group + .btn-group { + margin-left: -1px; +} +.btn-toolbar { + margin-left: -5px; +} +.btn-toolbar .btn, +.btn-toolbar .btn-group, +.btn-toolbar .input-group { + float: left; +} +.btn-toolbar > .btn, +.btn-toolbar > .btn-group, +.btn-toolbar > .input-group { + margin-left: 5px; +} +.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { + border-radius: 0; +} +.btn-group > .btn:first-child { + margin-left: 0; +} +.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { + border-bottom-right-radius: 0; + border-top-right-radius: 0; +} +.btn-group > .btn:last-child:not(:first-child), +.btn-group > .dropdown-toggle:not(:first-child) { + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} +.btn-group > .btn-group { + float: left; +} +.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child, +.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-bottom-right-radius: 0; + border-top-right-radius: 0; +} +.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} +.btn-group > .btn + .dropdown-toggle { + padding-left: 8px; + padding-right: 8px; +} +.btn-group > .btn-lg + .dropdown-toggle { + padding-left: 12px; + padding-right: 12px; +} +.btn-group.open .dropdown-toggle { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); +} +.btn-group.open .dropdown-toggle.btn-link { + -webkit-box-shadow: none; + box-shadow: none; +} +.btn .caret { + margin-left: 0; +} +.btn-lg .caret { + border-width: 5px 5px 0; + border-bottom-width: 0; +} +.dropup .btn-lg .caret { + border-width: 0 5px 5px; +} +.btn-group-vertical > .btn, +.btn-group-vertical > .btn-group, +.btn-group-vertical > .btn-group > .btn { + display: block; + float: none; + width: 100%; + max-width: 100%; +} +.btn-group-vertical > .btn-group > .btn { + float: none; +} +.btn-group-vertical > .btn + .btn, +.btn-group-vertical > .btn + .btn-group, +.btn-group-vertical > .btn-group + .btn, +.btn-group-vertical > .btn-group + .btn-group { + margin-top: -1px; + margin-left: 0; +} +.btn-group-vertical > .btn:not(:first-child):not(:last-child) { + border-radius: 0; +} +.btn-group-vertical > .btn:first-child:not(:last-child) { + border-top-right-radius: 4px; + border-top-left-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn:last-child:not(:first-child) { + border-top-right-radius: 0; + border-top-left-radius: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-top-right-radius: 0; + border-top-left-radius: 0; +} +.btn-group-justified { + display: table; + width: 100%; + table-layout: fixed; + border-collapse: separate; +} +.btn-group-justified > .btn, +.btn-group-justified > .btn-group { + float: none; + display: table-cell; + width: 1%; +} +.btn-group-justified > .btn-group .btn { + width: 100%; +} +.btn-group-justified > .btn-group .dropdown-menu { + left: auto; +} +[data-toggle="buttons"] > .btn input[type="radio"], +[data-toggle="buttons"] > .btn-group > .btn input[type="radio"], +[data-toggle="buttons"] > .btn input[type="checkbox"], +[data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] { + position: absolute; + clip: rect(0, 0, 0, 0); + pointer-events: none; +} +.input-group { + position: relative; + display: table; + border-collapse: separate; +} +.input-group[class*="col-"] { + float: none; + padding-left: 0; + padding-right: 0; +} +.input-group .form-control { + position: relative; + z-index: 2; + float: left; + width: 100%; + margin-bottom: 0; +} +.input-group .form-control:focus { + z-index: 3; +} +.input-group-lg > .form-control, +.input-group-lg > .input-group-addon, +.input-group-lg > .input-group-btn > .btn { + height: 66px; + padding: 20px 30px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +select.input-group-lg > .form-control, +select.input-group-lg > .input-group-addon, +select.input-group-lg > .input-group-btn > .btn { + height: 66px; + line-height: 66px; +} +textarea.input-group-lg > .form-control, +textarea.input-group-lg > .input-group-addon, +textarea.input-group-lg > .input-group-btn > .btn, +select[multiple].input-group-lg > .form-control, +select[multiple].input-group-lg > .input-group-addon, +select[multiple].input-group-lg > .input-group-btn > .btn { + height: auto; +} +.input-group-sm > .form-control, +.input-group-sm > .input-group-addon, +.input-group-sm > .input-group-btn > .btn { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-group-sm > .form-control, +select.input-group-sm > .input-group-addon, +select.input-group-sm > .input-group-btn > .btn { + height: 30px; + line-height: 30px; +} +textarea.input-group-sm > .form-control, +textarea.input-group-sm > .input-group-addon, +textarea.input-group-sm > .input-group-btn > .btn, +select[multiple].input-group-sm > .form-control, +select[multiple].input-group-sm > .input-group-addon, +select[multiple].input-group-sm > .input-group-btn > .btn { + height: auto; +} +.input-group-addon, +.input-group-btn, +.input-group .form-control { + display: table-cell; +} +.input-group-addon:not(:first-child):not(:last-child), +.input-group-btn:not(:first-child):not(:last-child), +.input-group .form-control:not(:first-child):not(:last-child) { + border-radius: 0; +} +.input-group-addon, +.input-group-btn { + width: 1%; + white-space: nowrap; + vertical-align: middle; +} +.input-group-addon { + padding: 12px 16px; + font-size: 14px; + font-weight: normal; + line-height: 1; + color: #3e3f3a; + text-align: center; + background-color: #f8f5f0; + border: 1px solid #dfd7ca; + border-radius: 4px; +} +.input-group-addon.input-sm { + padding: 5px 10px; + font-size: 12px; + border-radius: 3px; +} +.input-group-addon.input-lg { + padding: 20px 30px; + font-size: 18px; + border-radius: 6px; +} +.input-group-addon input[type="radio"], +.input-group-addon input[type="checkbox"] { + margin-top: 0; +} +.input-group .form-control:first-child, +.input-group-addon:first-child, +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group > .btn, +.input-group-btn:first-child > .dropdown-toggle, +.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { + border-bottom-right-radius: 0; + border-top-right-radius: 0; +} +.input-group-addon:first-child { + border-right: 0; +} +.input-group .form-control:last-child, +.input-group-addon:last-child, +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group > .btn, +.input-group-btn:last-child > .dropdown-toggle, +.input-group-btn:first-child > .btn:not(:first-child), +.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} +.input-group-addon:last-child { + border-left: 0; +} +.input-group-btn { + position: relative; + font-size: 0; + white-space: nowrap; +} +.input-group-btn > .btn { + position: relative; +} +.input-group-btn > .btn + .btn { + margin-left: -1px; +} +.input-group-btn > .btn:hover, +.input-group-btn > .btn:focus, +.input-group-btn > .btn:active { + z-index: 2; +} +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group { + margin-right: -1px; +} +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group { + z-index: 2; + margin-left: -1px; +} +.nav { + margin-bottom: 0; + padding-left: 0; + list-style: none; +} +.nav > li { + position: relative; + display: block; +} +.nav > li > a { + position: relative; + display: block; + padding: 10px 15px; +} +.nav > li > a:hover, +.nav > li > a:focus { + text-decoration: none; + background-color: #f8f5f0; +} +.nav > li.disabled > a { + color: #dfd7ca; +} +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #dfd7ca; + text-decoration: none; + background-color: transparent; + cursor: not-allowed; +} +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #f8f5f0; + border-color: #93c54b; +} +.nav .nav-divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.nav > li > a > img { + max-width: none; +} +.nav-tabs { + border-bottom: 1px solid #dfd7ca; +} +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.42857143; + border: 1px solid transparent; + border-radius: 4px 4px 0 0; +} +.nav-tabs > li > a:hover { + border-color: #dfd7ca #dfd7ca #dfd7ca; +} +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #98978b; + background-color: #ffffff; + border: 1px solid #dfd7ca; + border-bottom-color: transparent; + cursor: default; +} +.nav-tabs.nav-justified { + width: 100%; + border-bottom: 0; +} +.nav-tabs.nav-justified > li { + float: none; +} +.nav-tabs.nav-justified > li > a { + text-align: center; + margin-bottom: 5px; +} +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-tabs.nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs.nav-justified > .active > a, +.nav-tabs.nav-justified > .active > a:hover, +.nav-tabs.nav-justified > .active > a:focus { + border: 1px solid #dfd7ca; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid #dfd7ca; + border-radius: 4px 4px 0 0; + } + .nav-tabs.nav-justified > .active > a, + .nav-tabs.nav-justified > .active > a:hover, + .nav-tabs.nav-justified > .active > a:focus { + border-bottom-color: #ffffff; + } +} +.nav-pills > li { + float: left; +} +.nav-pills > li > a { + border-radius: 4px; +} +.nav-pills > li + li { + margin-left: 2px; +} +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + color: #98978b; + background-color: #f8f5f0; +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0; +} +.nav-justified { + width: 100%; +} +.nav-justified > li { + float: none; +} +.nav-justified > li > a { + text-align: center; + margin-bottom: 5px; +} +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs-justified { + border-bottom: 0; +} +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs-justified > .active > a, +.nav-tabs-justified > .active > a:hover, +.nav-tabs-justified > .active > a:focus { + border: 1px solid #dfd7ca; +} +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid #dfd7ca; + border-radius: 4px 4px 0 0; + } + .nav-tabs-justified > .active > a, + .nav-tabs-justified > .active > a:hover, + .nav-tabs-justified > .active > a:focus { + border-bottom-color: #ffffff; + } +} +.tab-content > .tab-pane { + display: none; +} +.tab-content > .active { + display: block; +} +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-right-radius: 0; + border-top-left-radius: 0; +} +.navbar { + position: relative; + min-height: 60px; + margin-bottom: 20px; + border: 1px solid transparent; +} +@media (min-width: 768px) { + .navbar { + border-radius: 4px; + } +} +@media (min-width: 768px) { + .navbar-header { + float: left; + } +} +.navbar-collapse { + overflow-x: visible; + padding-right: 15px; + padding-left: 15px; + border-top: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1); + -webkit-overflow-scrolling: touch; +} +.navbar-collapse.in { + overflow-y: auto; +} +@media (min-width: 768px) { + .navbar-collapse { + width: auto; + border-top: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important; + } + .navbar-collapse.in { + overflow-y: visible; + } + .navbar-fixed-top .navbar-collapse, + .navbar-static-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + padding-left: 0; + padding-right: 0; + } +} +.navbar-fixed-top .navbar-collapse, +.navbar-fixed-bottom .navbar-collapse { + max-height: 340px; +} +@media (max-device-width: 480px) and (orientation: landscape) { + .navbar-fixed-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + max-height: 200px; + } +} +.container > .navbar-header, +.container-fluid > .navbar-header, +.container > .navbar-collapse, +.container-fluid > .navbar-collapse { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .container > .navbar-header, + .container-fluid > .navbar-header, + .container > .navbar-collapse, + .container-fluid > .navbar-collapse { + margin-right: 0; + margin-left: 0; + } +} +.navbar-static-top { + z-index: 1000; + border-width: 0 0 1px; +} +@media (min-width: 768px) { + .navbar-static-top { + border-radius: 0; + } +} +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; +} +@media (min-width: 768px) { + .navbar-fixed-top, + .navbar-fixed-bottom { + border-radius: 0; + } +} +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px; +} +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; + border-width: 1px 0 0; +} +.navbar-brand { + float: left; + padding: 20px 15px; + font-size: 18px; + line-height: 20px; + height: 60px; +} +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none; +} +.navbar-brand > img { + display: block; +} +@media (min-width: 768px) { + .navbar > .container .navbar-brand, + .navbar > .container-fluid .navbar-brand { + margin-left: -15px; + } +} +.navbar-toggle { + position: relative; + float: right; + margin-right: 15px; + padding: 9px 10px; + margin-top: 13px; + margin-bottom: 13px; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} +.navbar-toggle:focus { + outline: 0; +} +.navbar-toggle .icon-bar { + display: block; + width: 22px; + height: 2px; + border-radius: 1px; +} +.navbar-toggle .icon-bar + .icon-bar { + margin-top: 4px; +} +@media (min-width: 768px) { + .navbar-toggle { + display: none; + } +} +.navbar-nav { + margin: 10px -15px; +} +.navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 20px; +} +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu { + position: static; + float: none; + width: auto; + margin-top: 0; + background-color: transparent; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-nav .open .dropdown-menu > li > a, + .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 5px 15px 5px 25px; + } + .navbar-nav .open .dropdown-menu > li > a { + line-height: 20px; + } + .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-nav .open .dropdown-menu > li > a:focus { + background-image: none; + } +} +@media (min-width: 768px) { + .navbar-nav { + float: left; + margin: 0; + } + .navbar-nav > li { + float: left; + } + .navbar-nav > li > a { + padding-top: 20px; + padding-bottom: 20px; + } +} +.navbar-form { + margin-left: -15px; + margin-right: -15px; + padding: 10px 15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + margin-top: 7px; + margin-bottom: 7px; +} +@media (min-width: 768px) { + .navbar-form .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .navbar-form .form-control-static { + display: inline-block; + } + .navbar-form .input-group { + display: inline-table; + vertical-align: middle; + } + .navbar-form .input-group .input-group-addon, + .navbar-form .input-group .input-group-btn, + .navbar-form .input-group .form-control { + width: auto; + } + .navbar-form .input-group > .form-control { + width: 100%; + } + .navbar-form .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio, + .navbar-form .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio label, + .navbar-form .checkbox label { + padding-left: 0; + } + .navbar-form .radio input[type="radio"], + .navbar-form .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + .navbar-form .has-feedback .form-control-feedback { + top: 0; + } +} +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 5px; + } + .navbar-form .form-group:last-child { + margin-bottom: 0; + } +} +@media (min-width: 768px) { + .navbar-form { + width: auto; + border: 0; + margin-left: 0; + margin-right: 0; + padding-top: 0; + padding-bottom: 0; + -webkit-box-shadow: none; + box-shadow: none; + } +} +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + border-top-right-radius: 0; + border-top-left-radius: 0; +} +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + margin-bottom: 0; + border-top-right-radius: 4px; + border-top-left-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.navbar-btn { + margin-top: 7px; + margin-bottom: 7px; +} +.navbar-btn.btn-sm { + margin-top: 15px; + margin-bottom: 15px; +} +.navbar-btn.btn-xs { + margin-top: 19px; + margin-bottom: 19px; +} +.navbar-text { + margin-top: 20px; + margin-bottom: 20px; +} +@media (min-width: 768px) { + .navbar-text { + float: left; + margin-left: 15px; + margin-right: 15px; + } +} +@media (min-width: 768px) { + .navbar-left { + float: left !important; + } + .navbar-right { + float: right !important; + margin-right: -15px; + } + .navbar-right ~ .navbar-right { + margin-right: 0; + } +} +.navbar-default { + background-color: #3e3f3a; + border-color: #3e3f3a; +} +.navbar-default .navbar-brand { + color: #ffffff; +} +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { + color: #ffffff; + background-color: transparent; +} +.navbar-default .navbar-text { + color: #8e8c84; +} +.navbar-default .navbar-nav > li > a { + color: #98978b; +} +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + color: #ffffff; + background-color: transparent; +} +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #ffffff; + background-color: #393a35; +} +.navbar-default .navbar-nav > .disabled > a, +.navbar-default .navbar-nav > .disabled > a:hover, +.navbar-default .navbar-nav > .disabled > a:focus { + color: #cccccc; + background-color: transparent; +} +.navbar-default .navbar-toggle { + border-color: transparent; +} +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: #393a35; +} +.navbar-default .navbar-toggle .icon-bar { + background-color: #98978b; +} +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #3e3f3a; +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .open > a:hover, +.navbar-default .navbar-nav > .open > a:focus { + background-color: #393a35; + color: #ffffff; +} +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #98978b; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: #ffffff; + background-color: transparent; + } + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #ffffff; + background-color: #393a35; + } + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #cccccc; + background-color: transparent; + } +} +.navbar-default .navbar-link { + color: #98978b; +} +.navbar-default .navbar-link:hover { + color: #ffffff; +} +.navbar-default .btn-link { + color: #98978b; +} +.navbar-default .btn-link:hover, +.navbar-default .btn-link:focus { + color: #ffffff; +} +.navbar-default .btn-link[disabled]:hover, +fieldset[disabled] .navbar-default .btn-link:hover, +.navbar-default .btn-link[disabled]:focus, +fieldset[disabled] .navbar-default .btn-link:focus { + color: #cccccc; +} +.navbar-inverse { + background-color: #93c54b; + border-color: #93c54b; +} +.navbar-inverse .navbar-brand { + color: #ffffff; +} +.navbar-inverse .navbar-brand:hover, +.navbar-inverse .navbar-brand:focus { + color: #ffffff; + background-color: transparent; +} +.navbar-inverse .navbar-text { + color: #dfd7ca; +} +.navbar-inverse .navbar-nav > li > a { + color: #6b9430; +} +.navbar-inverse .navbar-nav > li > a:hover, +.navbar-inverse .navbar-nav > li > a:focus { + color: #ffffff; + background-color: transparent; +} +.navbar-inverse .navbar-nav > .active > a, +.navbar-inverse .navbar-nav > .active > a:hover, +.navbar-inverse .navbar-nav > .active > a:focus { + color: #ffffff; + background-color: #89be3d; +} +.navbar-inverse .navbar-nav > .disabled > a, +.navbar-inverse .navbar-nav > .disabled > a:hover, +.navbar-inverse .navbar-nav > .disabled > a:focus { + color: #444444; + background-color: transparent; +} +.navbar-inverse .navbar-toggle { + border-color: transparent; +} +.navbar-inverse .navbar-toggle:hover, +.navbar-inverse .navbar-toggle:focus { + background-color: #89be3d; +} +.navbar-inverse .navbar-toggle .icon-bar { + background-color: #6b9430; +} +.navbar-inverse .navbar-collapse, +.navbar-inverse .navbar-form { + border-color: #81b33a; +} +.navbar-inverse .navbar-nav > .open > a, +.navbar-inverse .navbar-nav > .open > a:hover, +.navbar-inverse .navbar-nav > .open > a:focus { + background-color: #89be3d; + color: #ffffff; +} +@media (max-width: 767px) { + .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { + border-color: #93c54b; + } + .navbar-inverse .navbar-nav .open .dropdown-menu .divider { + background-color: #93c54b; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { + color: #6b9430; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { + color: #ffffff; + background-color: transparent; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #ffffff; + background-color: #89be3d; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #444444; + background-color: transparent; + } +} +.navbar-inverse .navbar-link { + color: #6b9430; +} +.navbar-inverse .navbar-link:hover { + color: #ffffff; +} +.navbar-inverse .btn-link { + color: #6b9430; +} +.navbar-inverse .btn-link:hover, +.navbar-inverse .btn-link:focus { + color: #ffffff; +} +.navbar-inverse .btn-link[disabled]:hover, +fieldset[disabled] .navbar-inverse .btn-link:hover, +.navbar-inverse .btn-link[disabled]:focus, +fieldset[disabled] .navbar-inverse .btn-link:focus { + color: #444444; +} +.breadcrumb { + padding: 8px 15px; + margin-bottom: 20px; + list-style: none; + background-color: #f8f5f0; + border-radius: 4px; +} +.breadcrumb > li { + display: inline-block; +} +.breadcrumb > li + li:before { + content: "/\00a0"; + padding: 0 5px; + color: #dfd7ca; +} +.breadcrumb > .active { + color: #98978b; +} +.pagination { + display: inline-block; + padding-left: 0; + margin: 20px 0; + border-radius: 4px; +} +.pagination > li { + display: inline; +} +.pagination > li > a, +.pagination > li > span { + position: relative; + float: left; + padding: 12px 16px; + line-height: 1.42857143; + text-decoration: none; + color: #98978b; + background-color: #f8f5f0; + border: 1px solid #dfd7ca; + margin-left: -1px; +} +.pagination > li:first-child > a, +.pagination > li:first-child > span { + margin-left: 0; + border-bottom-left-radius: 4px; + border-top-left-radius: 4px; +} +.pagination > li:last-child > a, +.pagination > li:last-child > span { + border-bottom-right-radius: 4px; + border-top-right-radius: 4px; +} +.pagination > li > a:hover, +.pagination > li > span:hover, +.pagination > li > a:focus, +.pagination > li > span:focus { + z-index: 2; + color: #8e8c84; + background-color: #dfd7ca; + border-color: #dfd7ca; +} +.pagination > .active > a, +.pagination > .active > span, +.pagination > .active > a:hover, +.pagination > .active > span:hover, +.pagination > .active > a:focus, +.pagination > .active > span:focus { + z-index: 3; + color: #8e8c84; + background-color: #dfd7ca; + border-color: #dfd7ca; + cursor: default; +} +.pagination > .disabled > span, +.pagination > .disabled > span:hover, +.pagination > .disabled > span:focus, +.pagination > .disabled > a, +.pagination > .disabled > a:hover, +.pagination > .disabled > a:focus { + color: #dfd7ca; + background-color: #f8f5f0; + border-color: #dfd7ca; + cursor: not-allowed; +} +.pagination-lg > li > a, +.pagination-lg > li > span { + padding: 20px 30px; + font-size: 18px; + line-height: 1.3333333; +} +.pagination-lg > li:first-child > a, +.pagination-lg > li:first-child > span { + border-bottom-left-radius: 6px; + border-top-left-radius: 6px; +} +.pagination-lg > li:last-child > a, +.pagination-lg > li:last-child > span { + border-bottom-right-radius: 6px; + border-top-right-radius: 6px; +} +.pagination-sm > li > a, +.pagination-sm > li > span { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; +} +.pagination-sm > li:first-child > a, +.pagination-sm > li:first-child > span { + border-bottom-left-radius: 3px; + border-top-left-radius: 3px; +} +.pagination-sm > li:last-child > a, +.pagination-sm > li:last-child > span { + border-bottom-right-radius: 3px; + border-top-right-radius: 3px; +} +.pager { + padding-left: 0; + margin: 20px 0; + list-style: none; + text-align: center; +} +.pager li { + display: inline; +} +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #f8f5f0; + border: 1px solid #dfd7ca; + border-radius: 15px; +} +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + background-color: #dfd7ca; +} +.pager .next > a, +.pager .next > span { + float: right; +} +.pager .previous > a, +.pager .previous > span { + float: left; +} +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #dfd7ca; + background-color: #f8f5f0; + cursor: not-allowed; +} +.label { + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: bold; + line-height: 1; + color: #ffffff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em; +} +a.label:hover, +a.label:focus { + color: #ffffff; + text-decoration: none; + cursor: pointer; +} +.label:empty { + display: none; +} +.btn .label { + position: relative; + top: -1px; +} +.label-default { + background-color: #3e3f3a; +} +.label-default[href]:hover, +.label-default[href]:focus { + background-color: #242422; +} +.label-primary { + background-color: #325d88; +} +.label-primary[href]:hover, +.label-primary[href]:focus { + background-color: #244363; +} +.label-success { + background-color: #93c54b; +} +.label-success[href]:hover, +.label-success[href]:focus { + background-color: #79a736; +} +.label-info { + background-color: #29abe0; +} +.label-info[href]:hover, +.label-info[href]:focus { + background-color: #1b8dbb; +} +.label-warning { + background-color: #f47c3c; +} +.label-warning[href]:hover, +.label-warning[href]:focus { + background-color: #ef5c0e; +} +.label-danger { + background-color: #d9534f; +} +.label-danger[href]:hover, +.label-danger[href]:focus { + background-color: #c9302c; +} +.badge { + display: inline-block; + min-width: 10px; + padding: 3px 7px; + font-size: 12px; + font-weight: normal; + color: #ffffff; + line-height: 1; + vertical-align: middle; + white-space: nowrap; + text-align: center; + background-color: #93c54b; + border-radius: 10px; +} +.badge:empty { + display: none; +} +.btn .badge { + position: relative; + top: -1px; +} +.btn-xs .badge, +.btn-group-xs > .btn .badge { + top: 0; + padding: 1px 5px; +} +a.badge:hover, +a.badge:focus { + color: #ffffff; + text-decoration: none; + cursor: pointer; +} +.list-group-item.active > .badge, +.nav-pills > .active > a > .badge { + color: #ffffff; + background-color: #93c54b; +} +.list-group-item > .badge { + float: right; +} +.list-group-item > .badge + .badge { + margin-right: 5px; +} +.nav-pills > li > a > .badge { + margin-left: 3px; +} +.jumbotron { + padding-top: 30px; + padding-bottom: 30px; + margin-bottom: 30px; + color: inherit; + background-color: #f8f5f0; +} +.jumbotron h1, +.jumbotron .h1 { + color: inherit; +} +.jumbotron p { + margin-bottom: 15px; + font-size: 21px; + font-weight: 200; +} +.jumbotron > hr { + border-top-color: #e8decd; +} +.container .jumbotron, +.container-fluid .jumbotron { + border-radius: 6px; + padding-left: 15px; + padding-right: 15px; +} +.jumbotron .container { + max-width: 100%; +} +@media screen and (min-width: 768px) { + .jumbotron { + padding-top: 48px; + padding-bottom: 48px; + } + .container .jumbotron, + .container-fluid .jumbotron { + padding-left: 60px; + padding-right: 60px; + } + .jumbotron h1, + .jumbotron .h1 { + font-size: 63px; + } +} +.thumbnail { + display: block; + padding: 4px; + margin-bottom: 20px; + line-height: 1.42857143; + background-color: #f8f5f0; + border: 1px solid #dfd7ca; + border-radius: 4px; + -webkit-transition: border 0.2s ease-in-out; + -o-transition: border 0.2s ease-in-out; + transition: border 0.2s ease-in-out; +} +.thumbnail > img, +.thumbnail a > img { + margin-left: auto; + margin-right: auto; +} +a.thumbnail:hover, +a.thumbnail:focus, +a.thumbnail.active { + border-color: #93c54b; +} +.thumbnail .caption { + padding: 9px; + color: #3e3f3a; +} +.alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px; +} +.alert h4 { + margin-top: 0; + color: inherit; +} +.alert .alert-link { + font-weight: bold; +} +.alert > p, +.alert > ul { + margin-bottom: 0; +} +.alert > p + p { + margin-top: 5px; +} +.alert-dismissable, +.alert-dismissible { + padding-right: 35px; +} +.alert-dismissable .close, +.alert-dismissible .close { + position: relative; + top: -2px; + right: -21px; + color: inherit; +} +.alert-success { + background-color: #93c54b; + border-color: transparent; + color: #ffffff; +} +.alert-success hr { + border-top-color: rgba(0, 0, 0, 0); +} +.alert-success .alert-link { + color: #e6e6e6; +} +.alert-info { + background-color: #29abe0; + border-color: transparent; + color: #ffffff; +} +.alert-info hr { + border-top-color: rgba(0, 0, 0, 0); +} +.alert-info .alert-link { + color: #e6e6e6; +} +.alert-warning { + background-color: #f47c3c; + border-color: transparent; + color: #ffffff; +} +.alert-warning hr { + border-top-color: rgba(0, 0, 0, 0); +} +.alert-warning .alert-link { + color: #e6e6e6; +} +.alert-danger { + background-color: #d9534f; + border-color: transparent; + color: #ffffff; +} +.alert-danger hr { + border-top-color: rgba(0, 0, 0, 0); +} +.alert-danger .alert-link { + color: #e6e6e6; +} +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@-o-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +.progress { + overflow: hidden; + height: 20px; + margin-bottom: 20px; + background-color: #f5f5f5; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); +} +.progress-bar { + float: left; + width: 0%; + height: 100%; + font-size: 12px; + line-height: 20px; + color: #ffffff; + text-align: center; + background-color: #325d88; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -webkit-transition: width 0.6s ease; + -o-transition: width 0.6s ease; + transition: width 0.6s ease; +} +.progress-striped .progress-bar, +.progress-bar-striped { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + -webkit-background-size: 40px 40px; + background-size: 40px 40px; +} +.progress.active .progress-bar, +.progress-bar.active { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} +.progress-bar-success { + background-color: #93c54b; +} +.progress-striped .progress-bar-success { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.progress-bar-info { + background-color: #29abe0; +} +.progress-striped .progress-bar-info { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.progress-bar-warning { + background-color: #f47c3c; +} +.progress-striped .progress-bar-warning { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.progress-bar-danger { + background-color: #d9534f; +} +.progress-striped .progress-bar-danger { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.media { + margin-top: 15px; +} +.media:first-child { + margin-top: 0; +} +.media, +.media-body { + zoom: 1; + overflow: hidden; +} +.media-body { + width: 10000px; +} +.media-object { + display: block; +} +.media-object.img-thumbnail { + max-width: none; +} +.media-right, +.media > .pull-right { + padding-left: 10px; +} +.media-left, +.media > .pull-left { + padding-right: 10px; +} +.media-left, +.media-right, +.media-body { + display: table-cell; + vertical-align: top; +} +.media-middle { + vertical-align: middle; +} +.media-bottom { + vertical-align: bottom; +} +.media-heading { + margin-top: 0; + margin-bottom: 5px; +} +.media-list { + padding-left: 0; + list-style: none; +} +.list-group { + margin-bottom: 20px; + padding-left: 0; +} +.list-group-item { + position: relative; + display: block; + padding: 10px 15px; + margin-bottom: -1px; + background-color: #ffffff; + border: 1px solid #dfd7ca; +} +.list-group-item:first-child { + border-top-right-radius: 4px; + border-top-left-radius: 4px; +} +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +a.list-group-item, +button.list-group-item { + color: #3e3f3a; +} +a.list-group-item .list-group-item-heading, +button.list-group-item .list-group-item-heading { + color: inherit; +} +a.list-group-item:hover, +button.list-group-item:hover, +a.list-group-item:focus, +button.list-group-item:focus { + text-decoration: none; + color: #3e3f3a; + background-color: #f8f5f0; +} +button.list-group-item { + width: 100%; + text-align: left; +} +.list-group-item.disabled, +.list-group-item.disabled:hover, +.list-group-item.disabled:focus { + background-color: #f8f5f0; + color: #dfd7ca; + cursor: not-allowed; +} +.list-group-item.disabled .list-group-item-heading, +.list-group-item.disabled:hover .list-group-item-heading, +.list-group-item.disabled:focus .list-group-item-heading { + color: inherit; +} +.list-group-item.disabled .list-group-item-text, +.list-group-item.disabled:hover .list-group-item-text, +.list-group-item.disabled:focus .list-group-item-text { + color: #dfd7ca; +} +.list-group-item.active, +.list-group-item.active:hover, +.list-group-item.active:focus { + z-index: 2; + color: #3e3f3a; + background-color: #f8f5f0; + border-color: #dfd7ca; +} +.list-group-item.active .list-group-item-heading, +.list-group-item.active:hover .list-group-item-heading, +.list-group-item.active:focus .list-group-item-heading, +.list-group-item.active .list-group-item-heading > small, +.list-group-item.active:hover .list-group-item-heading > small, +.list-group-item.active:focus .list-group-item-heading > small, +.list-group-item.active .list-group-item-heading > .small, +.list-group-item.active:hover .list-group-item-heading > .small, +.list-group-item.active:focus .list-group-item-heading > .small { + color: inherit; +} +.list-group-item.active .list-group-item-text, +.list-group-item.active:hover .list-group-item-text, +.list-group-item.active:focus .list-group-item-text { + color: #3e3f3a; +} +.list-group-item-success { + color: #93c54b; + background-color: #dff0d8; +} +a.list-group-item-success, +button.list-group-item-success { + color: #93c54b; +} +a.list-group-item-success .list-group-item-heading, +button.list-group-item-success .list-group-item-heading { + color: inherit; +} +a.list-group-item-success:hover, +button.list-group-item-success:hover, +a.list-group-item-success:focus, +button.list-group-item-success:focus { + color: #93c54b; + background-color: #d0e9c6; +} +a.list-group-item-success.active, +button.list-group-item-success.active, +a.list-group-item-success.active:hover, +button.list-group-item-success.active:hover, +a.list-group-item-success.active:focus, +button.list-group-item-success.active:focus { + color: #fff; + background-color: #93c54b; + border-color: #93c54b; +} +.list-group-item-info { + color: #29abe0; + background-color: #d9edf7; +} +a.list-group-item-info, +button.list-group-item-info { + color: #29abe0; +} +a.list-group-item-info .list-group-item-heading, +button.list-group-item-info .list-group-item-heading { + color: inherit; +} +a.list-group-item-info:hover, +button.list-group-item-info:hover, +a.list-group-item-info:focus, +button.list-group-item-info:focus { + color: #29abe0; + background-color: #c4e3f3; +} +a.list-group-item-info.active, +button.list-group-item-info.active, +a.list-group-item-info.active:hover, +button.list-group-item-info.active:hover, +a.list-group-item-info.active:focus, +button.list-group-item-info.active:focus { + color: #fff; + background-color: #29abe0; + border-color: #29abe0; +} +.list-group-item-warning { + color: #f47c3c; + background-color: #fcf8e3; +} +a.list-group-item-warning, +button.list-group-item-warning { + color: #f47c3c; +} +a.list-group-item-warning .list-group-item-heading, +button.list-group-item-warning .list-group-item-heading { + color: inherit; +} +a.list-group-item-warning:hover, +button.list-group-item-warning:hover, +a.list-group-item-warning:focus, +button.list-group-item-warning:focus { + color: #f47c3c; + background-color: #faf2cc; +} +a.list-group-item-warning.active, +button.list-group-item-warning.active, +a.list-group-item-warning.active:hover, +button.list-group-item-warning.active:hover, +a.list-group-item-warning.active:focus, +button.list-group-item-warning.active:focus { + color: #fff; + background-color: #f47c3c; + border-color: #f47c3c; +} +.list-group-item-danger { + color: #d9534f; + background-color: #f2dede; +} +a.list-group-item-danger, +button.list-group-item-danger { + color: #d9534f; +} +a.list-group-item-danger .list-group-item-heading, +button.list-group-item-danger .list-group-item-heading { + color: inherit; +} +a.list-group-item-danger:hover, +button.list-group-item-danger:hover, +a.list-group-item-danger:focus, +button.list-group-item-danger:focus { + color: #d9534f; + background-color: #ebcccc; +} +a.list-group-item-danger.active, +button.list-group-item-danger.active, +a.list-group-item-danger.active:hover, +button.list-group-item-danger.active:hover, +a.list-group-item-danger.active:focus, +button.list-group-item-danger.active:focus { + color: #fff; + background-color: #d9534f; + border-color: #d9534f; +} +.list-group-item-heading { + margin-top: 0; + margin-bottom: 5px; +} +.list-group-item-text { + margin-bottom: 0; + line-height: 1.3; +} +.panel { + margin-bottom: 20px; + background-color: #ffffff; + border: 1px solid transparent; + border-radius: 4px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); +} +.panel-body { + padding: 15px; +} +.panel-heading { + padding: 10px 15px; + border-bottom: 1px solid transparent; + border-top-right-radius: 3px; + border-top-left-radius: 3px; +} +.panel-heading > .dropdown .dropdown-toggle { + color: inherit; +} +.panel-title { + margin-top: 0; + margin-bottom: 0; + font-size: 16px; + color: inherit; +} +.panel-title > a, +.panel-title > small, +.panel-title > .small, +.panel-title > small > a, +.panel-title > .small > a { + color: inherit; +} +.panel-footer { + padding: 10px 15px; + background-color: #f8f5f0; + border-top: 1px solid #dfd7ca; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .list-group, +.panel > .panel-collapse > .list-group { + margin-bottom: 0; +} +.panel > .list-group .list-group-item, +.panel > .panel-collapse > .list-group .list-group-item { + border-width: 1px 0; + border-radius: 0; +} +.panel > .list-group:first-child .list-group-item:first-child, +.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { + border-top: 0; + border-top-right-radius: 3px; + border-top-left-radius: 3px; +} +.panel > .list-group:last-child .list-group-item:last-child, +.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { + border-bottom: 0; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child { + border-top-right-radius: 0; + border-top-left-radius: 0; +} +.panel-heading + .list-group .list-group-item:first-child { + border-top-width: 0; +} +.list-group + .panel-footer { + border-top-width: 0; +} +.panel > .table, +.panel > .table-responsive > .table, +.panel > .panel-collapse > .table { + margin-bottom: 0; +} +.panel > .table caption, +.panel > .table-responsive > .table caption, +.panel > .panel-collapse > .table caption { + padding-left: 15px; + padding-right: 15px; +} +.panel > .table:first-child, +.panel > .table-responsive:first-child > .table:first-child { + border-top-right-radius: 3px; + border-top-left-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { + border-top-left-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { + border-top-right-radius: 3px; +} +.panel > .table:last-child, +.panel > .table-responsive:last-child > .table:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child { + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { + border-bottom-right-radius: 3px; +} +.panel > .panel-body + .table, +.panel > .panel-body + .table-responsive, +.panel > .table + .panel-body, +.panel > .table-responsive + .panel-body { + border-top: 1px solid #dfd7ca; +} +.panel > .table > tbody:first-child > tr:first-child th, +.panel > .table > tbody:first-child > tr:first-child td { + border-top: 0; +} +.panel > .table-bordered, +.panel > .table-responsive > .table-bordered { + border: 0; +} +.panel > .table-bordered > thead > tr > th:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, +.panel > .table-bordered > tbody > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, +.panel > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-bordered > thead > tr > td:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, +.panel > .table-bordered > tbody > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, +.panel > .table-bordered > tfoot > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; +} +.panel > .table-bordered > thead > tr > th:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, +.panel > .table-bordered > tbody > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, +.panel > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-bordered > thead > tr > td:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, +.panel > .table-bordered > tbody > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, +.panel > .table-bordered > tfoot > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; +} +.panel > .table-bordered > thead > tr:first-child > td, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > td, +.panel > .table-bordered > tbody > tr:first-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, +.panel > .table-bordered > thead > tr:first-child > th, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > th, +.panel > .table-bordered > tbody > tr:first-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { + border-bottom: 0; +} +.panel > .table-bordered > tbody > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, +.panel > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-bordered > tbody > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, +.panel > .table-bordered > tfoot > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { + border-bottom: 0; +} +.panel > .table-responsive { + border: 0; + margin-bottom: 0; +} +.panel-group { + margin-bottom: 20px; +} +.panel-group .panel { + margin-bottom: 0; + border-radius: 4px; +} +.panel-group .panel + .panel { + margin-top: 5px; +} +.panel-group .panel-heading { + border-bottom: 0; +} +.panel-group .panel-heading + .panel-collapse > .panel-body, +.panel-group .panel-heading + .panel-collapse > .list-group { + border-top: 1px solid #dfd7ca; +} +.panel-group .panel-footer { + border-top: 0; +} +.panel-group .panel-footer + .panel-collapse .panel-body { + border-bottom: 1px solid #dfd7ca; +} +.panel-default { + border-color: #dfd7ca; +} +.panel-default > .panel-heading { + color: #3e3f3a; + background-color: #f8f5f0; + border-color: #dfd7ca; +} +.panel-default > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #dfd7ca; +} +.panel-default > .panel-heading .badge { + color: #f8f5f0; + background-color: #3e3f3a; +} +.panel-default > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #dfd7ca; +} +.panel-primary { + border-color: #325d88; +} +.panel-primary > .panel-heading { + color: #ffffff; + background-color: #325d88; + border-color: #325d88; +} +.panel-primary > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #325d88; +} +.panel-primary > .panel-heading .badge { + color: #325d88; + background-color: #ffffff; +} +.panel-primary > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #325d88; +} +.panel-success { + border-color: #d6e9c6; +} +.panel-success > .panel-heading { + color: #93c54b; + background-color: #93c54b; + border-color: #d6e9c6; +} +.panel-success > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #d6e9c6; +} +.panel-success > .panel-heading .badge { + color: #93c54b; + background-color: #93c54b; +} +.panel-success > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #d6e9c6; +} +.panel-info { + border-color: #bce8f1; +} +.panel-info > .panel-heading { + color: #29abe0; + background-color: #29abe0; + border-color: #bce8f1; +} +.panel-info > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #bce8f1; +} +.panel-info > .panel-heading .badge { + color: #29abe0; + background-color: #29abe0; +} +.panel-info > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #bce8f1; +} +.panel-warning { + border-color: #faebcc; +} +.panel-warning > .panel-heading { + color: #f47c3c; + background-color: #f47c3c; + border-color: #faebcc; +} +.panel-warning > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #faebcc; +} +.panel-warning > .panel-heading .badge { + color: #f47c3c; + background-color: #f47c3c; +} +.panel-warning > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #faebcc; +} +.panel-danger { + border-color: #ebccd1; +} +.panel-danger > .panel-heading { + color: #d9534f; + background-color: #d9534f; + border-color: #ebccd1; +} +.panel-danger > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #ebccd1; +} +.panel-danger > .panel-heading .badge { + color: #d9534f; + background-color: #d9534f; +} +.panel-danger > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #ebccd1; +} +.embed-responsive { + position: relative; + display: block; + height: 0; + padding: 0; + overflow: hidden; +} +.embed-responsive .embed-responsive-item, +.embed-responsive iframe, +.embed-responsive embed, +.embed-responsive object, +.embed-responsive video { + position: absolute; + top: 0; + left: 0; + bottom: 0; + height: 100%; + width: 100%; + border: 0; +} +.embed-responsive-16by9 { + padding-bottom: 56.25%; +} +.embed-responsive-4by3 { + padding-bottom: 75%; +} +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f8f5f0; + border: 1px solid transparent; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); +} +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, 0.15); +} +.well-lg { + padding: 24px; + border-radius: 6px; +} +.well-sm { + padding: 9px; + border-radius: 3px; +} +.close { + float: right; + font-size: 21px; + font-weight: bold; + line-height: 1; + color: #000000; + text-shadow: 0 0 0 transparent; + opacity: 0.2; + filter: alpha(opacity=20); +} +.close:hover, +.close:focus { + color: #000000; + text-decoration: none; + cursor: pointer; + opacity: 0.5; + filter: alpha(opacity=50); +} +button.close { + padding: 0; + cursor: pointer; + background: transparent; + border: 0; + -webkit-appearance: none; +} +.modal-open { + overflow: hidden; +} +.modal { + display: none; + overflow: hidden; + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1050; + -webkit-overflow-scrolling: touch; + outline: 0; +} +.modal.fade .modal-dialog { + -webkit-transform: translate(0, -25%); + -ms-transform: translate(0, -25%); + -o-transform: translate(0, -25%); + transform: translate(0, -25%); + -webkit-transition: -webkit-transform 0.3s ease-out; + -o-transition: -o-transform 0.3s ease-out; + transition: transform 0.3s ease-out; +} +.modal.in .modal-dialog { + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + -o-transform: translate(0, 0); + transform: translate(0, 0); +} +.modal-open .modal { + overflow-x: hidden; + overflow-y: auto; +} +.modal-dialog { + position: relative; + width: auto; + margin: 10px; +} +.modal-content { + position: relative; + background-color: #ffffff; + border: 1px solid #f8f5f0; + border-radius: 6px; + -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5); + box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5); + -webkit-background-clip: padding-box; + background-clip: padding-box; + outline: 0; +} +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000000; +} +.modal-backdrop.fade { + opacity: 0; + filter: alpha(opacity=0); +} +.modal-backdrop.in { + opacity: 0.5; + filter: alpha(opacity=50); +} +.modal-header { + padding: 15px; + border-bottom: 1px solid #f8f5f0; +} +.modal-header .close { + margin-top: -2px; +} +.modal-title { + margin: 0; + line-height: 1.42857143; +} +.modal-body { + position: relative; + padding: 15px; +} +.modal-footer { + padding: 15px; + text-align: right; + border-top: 1px solid #f8f5f0; +} +.modal-footer .btn + .btn { + margin-left: 5px; + margin-bottom: 0; +} +.modal-footer .btn-group .btn + .btn { + margin-left: -1px; +} +.modal-footer .btn-block + .btn-block { + margin-left: 0; +} +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll; +} +@media (min-width: 768px) { + .modal-dialog { + width: 600px; + margin: 30px auto; + } + .modal-content { + -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); + } + .modal-sm { + width: 300px; + } +} +@media (min-width: 992px) { + .modal-lg { + width: 900px; + } +} +.tooltip { + position: absolute; + z-index: 1070; + display: block; + font-family: "Roboto", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-style: normal; + font-weight: normal; + letter-spacing: normal; + line-break: auto; + line-height: 1.42857143; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + white-space: normal; + word-break: normal; + word-spacing: normal; + word-wrap: normal; + font-size: 12px; + opacity: 0; + filter: alpha(opacity=0); +} +.tooltip.in { + opacity: 1; + filter: alpha(opacity=100); +} +.tooltip.top { + margin-top: -3px; + padding: 5px 0; +} +.tooltip.right { + margin-left: 3px; + padding: 0 5px; +} +.tooltip.bottom { + margin-top: 3px; + padding: 5px 0; +} +.tooltip.left { + margin-left: -3px; + padding: 0 5px; +} +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #ffffff; + text-align: center; + background-color: #3e3f3a; + border-radius: 4px; +} +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-width: 5px 5px 0; + border-top-color: #3e3f3a; +} +.tooltip.top-left .tooltip-arrow { + bottom: 0; + right: 5px; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #3e3f3a; +} +.tooltip.top-right .tooltip-arrow { + bottom: 0; + left: 5px; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #3e3f3a; +} +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-width: 5px 5px 5px 0; + border-right-color: #3e3f3a; +} +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-width: 5px 0 5px 5px; + border-left-color: #3e3f3a; +} +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-width: 0 5px 5px; + border-bottom-color: #3e3f3a; +} +.tooltip.bottom-left .tooltip-arrow { + top: 0; + right: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #3e3f3a; +} +.tooltip.bottom-right .tooltip-arrow { + top: 0; + left: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #3e3f3a; +} +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1060; + display: none; + max-width: 276px; + padding: 1px; + font-family: "Roboto", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-style: normal; + font-weight: normal; + letter-spacing: normal; + line-break: auto; + line-height: 1.42857143; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + white-space: normal; + word-break: normal; + word-spacing: normal; + word-wrap: normal; + font-size: 14px; + background-color: #ffffff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #dfd7ca; + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); +} +.popover.top { + margin-top: -10px; +} +.popover.right { + margin-left: 10px; +} +.popover.bottom { + margin-top: 10px; +} +.popover.left { + margin-left: -10px; +} +.popover-title { + margin: 0; + padding: 8px 14px; + font-size: 14px; + background-color: #f8f5f0; + border-bottom: 1px solid #f0e9df; + border-radius: 5px 5px 0 0; +} +.popover-content { + padding: 9px 14px; +} +.popover > .arrow, +.popover > .arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.popover > .arrow { + border-width: 11px; +} +.popover > .arrow:after { + border-width: 10px; + content: ""; +} +.popover.top > .arrow { + left: 50%; + margin-left: -11px; + border-bottom-width: 0; + border-top-color: #b9a78a; + border-top-color: #dfd7ca; + bottom: -11px; +} +.popover.top > .arrow:after { + content: " "; + bottom: 1px; + margin-left: -10px; + border-bottom-width: 0; + border-top-color: #ffffff; +} +.popover.right > .arrow { + top: 50%; + left: -11px; + margin-top: -11px; + border-left-width: 0; + border-right-color: #b9a78a; + border-right-color: #dfd7ca; +} +.popover.right > .arrow:after { + content: " "; + left: 1px; + bottom: -10px; + border-left-width: 0; + border-right-color: #ffffff; +} +.popover.bottom > .arrow { + left: 50%; + margin-left: -11px; + border-top-width: 0; + border-bottom-color: #b9a78a; + border-bottom-color: #dfd7ca; + top: -11px; +} +.popover.bottom > .arrow:after { + content: " "; + top: 1px; + margin-left: -10px; + border-top-width: 0; + border-bottom-color: #ffffff; +} +.popover.left > .arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-right-width: 0; + border-left-color: #b9a78a; + border-left-color: #dfd7ca; +} +.popover.left > .arrow:after { + content: " "; + right: 1px; + border-right-width: 0; + border-left-color: #ffffff; + bottom: -10px; +} +.carousel { + position: relative; +} +.carousel-inner { + position: relative; + overflow: hidden; + width: 100%; +} +.carousel-inner > .item { + display: none; + position: relative; + -webkit-transition: 0.6s ease-in-out left; + -o-transition: 0.6s ease-in-out left; + transition: 0.6s ease-in-out left; +} +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + line-height: 1; +} +@media all and (transform-3d), (-webkit-transform-3d) { + .carousel-inner > .item { + -webkit-transition: -webkit-transform 0.6s ease-in-out; + -o-transition: -o-transform 0.6s ease-in-out; + transition: transform 0.6s ease-in-out; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + -webkit-perspective: 1000px; + perspective: 1000px; + } + .carousel-inner > .item.next, + .carousel-inner > .item.active.right { + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + left: 0; + } + .carousel-inner > .item.prev, + .carousel-inner > .item.active.left { + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + left: 0; + } + .carousel-inner > .item.next.left, + .carousel-inner > .item.prev.right, + .carousel-inner > .item.active { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + left: 0; + } +} +.carousel-inner > .active, +.carousel-inner > .next, +.carousel-inner > .prev { + display: block; +} +.carousel-inner > .active { + left: 0; +} +.carousel-inner > .next, +.carousel-inner > .prev { + position: absolute; + top: 0; + width: 100%; +} +.carousel-inner > .next { + left: 100%; +} +.carousel-inner > .prev { + left: -100%; +} +.carousel-inner > .next.left, +.carousel-inner > .prev.right { + left: 0; +} +.carousel-inner > .active.left { + left: -100%; +} +.carousel-inner > .active.right { + left: 100%; +} +.carousel-control { + position: absolute; + top: 0; + left: 0; + bottom: 0; + width: 15%; + opacity: 0.5; + filter: alpha(opacity=50); + font-size: 20px; + color: #ffffff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); + background-color: rgba(0, 0, 0, 0); +} +.carousel-control.left { + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, 0.5)), to(rgba(0, 0, 0, 0.0001))); + background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); +} +.carousel-control.right { + left: auto; + right: 0; + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, 0.0001)), to(rgba(0, 0, 0, 0.5))); + background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); +} +.carousel-control:hover, +.carousel-control:focus { + outline: 0; + color: #ffffff; + text-decoration: none; + opacity: 0.9; + filter: alpha(opacity=90); +} +.carousel-control .icon-prev, +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-left, +.carousel-control .glyphicon-chevron-right { + position: absolute; + top: 50%; + margin-top: -10px; + z-index: 5; + display: inline-block; +} +.carousel-control .icon-prev, +.carousel-control .glyphicon-chevron-left { + left: 50%; + margin-left: -10px; +} +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-right { + right: 50%; + margin-right: -10px; +} +.carousel-control .icon-prev, +.carousel-control .icon-next { + width: 20px; + height: 20px; + line-height: 1; + font-family: serif; +} +.carousel-control .icon-prev:before { + content: '\2039'; +} +.carousel-control .icon-next:before { + content: '\203a'; +} +.carousel-indicators { + position: absolute; + bottom: 10px; + left: 50%; + z-index: 15; + width: 60%; + margin-left: -30%; + padding-left: 0; + list-style: none; + text-align: center; +} +.carousel-indicators li { + display: inline-block; + width: 10px; + height: 10px; + margin: 1px; + text-indent: -999px; + border: 1px solid #ffffff; + border-radius: 10px; + cursor: pointer; + background-color: #000 \9; + background-color: rgba(0, 0, 0, 0); +} +.carousel-indicators .active { + margin: 0; + width: 12px; + height: 12px; + background-color: #ffffff; +} +.carousel-caption { + position: absolute; + left: 15%; + right: 15%; + bottom: 20px; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #ffffff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); +} +.carousel-caption .btn { + text-shadow: none; +} +@media screen and (min-width: 768px) { + .carousel-control .glyphicon-chevron-left, + .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-prev, + .carousel-control .icon-next { + width: 30px; + height: 30px; + margin-top: -10px; + font-size: 30px; + } + .carousel-control .glyphicon-chevron-left, + .carousel-control .icon-prev { + margin-left: -10px; + } + .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-next { + margin-right: -10px; + } + .carousel-caption { + left: 20%; + right: 20%; + padding-bottom: 30px; + } + .carousel-indicators { + bottom: 20px; + } +} +.clearfix:before, +.clearfix:after, +.dl-horizontal dd:before, +.dl-horizontal dd:after, +.container:before, +.container:after, +.container-fluid:before, +.container-fluid:after, +.row:before, +.row:after, +.form-horizontal .form-group:before, +.form-horizontal .form-group:after, +.btn-toolbar:before, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:before, +.btn-group-vertical > .btn-group:after, +.nav:before, +.nav:after, +.navbar:before, +.navbar:after, +.navbar-header:before, +.navbar-header:after, +.navbar-collapse:before, +.navbar-collapse:after, +.pager:before, +.pager:after, +.panel-body:before, +.panel-body:after, +.modal-header:before, +.modal-header:after, +.modal-footer:before, +.modal-footer:after { + content: " "; + display: table; +} +.clearfix:after, +.dl-horizontal dd:after, +.container:after, +.container-fluid:after, +.row:after, +.form-horizontal .form-group:after, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:after, +.nav:after, +.navbar:after, +.navbar-header:after, +.navbar-collapse:after, +.pager:after, +.panel-body:after, +.modal-header:after, +.modal-footer:after { + clear: both; +} +.center-block { + display: block; + margin-left: auto; + margin-right: auto; +} +.pull-right { + float: right !important; +} +.pull-left { + float: left !important; +} +.hide { + display: none !important; +} +.show { + display: block !important; +} +.invisible { + visibility: hidden; +} +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.hidden { + display: none !important; +} +.affix { + position: fixed; +} +@-ms-viewport { + width: device-width; +} +.visible-xs, +.visible-sm, +.visible-md, +.visible-lg { + display: none !important; +} +.visible-xs-block, +.visible-xs-inline, +.visible-xs-inline-block, +.visible-sm-block, +.visible-sm-inline, +.visible-sm-inline-block, +.visible-md-block, +.visible-md-inline, +.visible-md-inline-block, +.visible-lg-block, +.visible-lg-inline, +.visible-lg-inline-block { + display: none !important; +} +@media (max-width: 767px) { + .visible-xs { + display: block !important; + } + table.visible-xs { + display: table !important; + } + tr.visible-xs { + display: table-row !important; + } + th.visible-xs, + td.visible-xs { + display: table-cell !important; + } +} +@media (max-width: 767px) { + .visible-xs-block { + display: block !important; + } +} +@media (max-width: 767px) { + .visible-xs-inline { + display: inline !important; + } +} +@media (max-width: 767px) { + .visible-xs-inline-block { + display: inline-block !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm { + display: block !important; + } + table.visible-sm { + display: table !important; + } + tr.visible-sm { + display: table-row !important; + } + th.visible-sm, + td.visible-sm { + display: table-cell !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-block { + display: block !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline { + display: inline !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline-block { + display: inline-block !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md { + display: block !important; + } + table.visible-md { + display: table !important; + } + tr.visible-md { + display: table-row !important; + } + th.visible-md, + td.visible-md { + display: table-cell !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-block { + display: block !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline { + display: inline !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline-block { + display: inline-block !important; + } +} +@media (min-width: 1200px) { + .visible-lg { + display: block !important; + } + table.visible-lg { + display: table !important; + } + tr.visible-lg { + display: table-row !important; + } + th.visible-lg, + td.visible-lg { + display: table-cell !important; + } +} +@media (min-width: 1200px) { + .visible-lg-block { + display: block !important; + } +} +@media (min-width: 1200px) { + .visible-lg-inline { + display: inline !important; + } +} +@media (min-width: 1200px) { + .visible-lg-inline-block { + display: inline-block !important; + } +} +@media (max-width: 767px) { + .hidden-xs { + display: none !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .hidden-sm { + display: none !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-md { + display: none !important; + } +} +@media (min-width: 1200px) { + .hidden-lg { + display: none !important; + } +} +.visible-print { + display: none !important; +} +@media print { + .visible-print { + display: block !important; + } + table.visible-print { + display: table !important; + } + tr.visible-print { + display: table-row !important; + } + th.visible-print, + td.visible-print { + display: table-cell !important; + } +} +.visible-print-block { + display: none !important; +} +@media print { + .visible-print-block { + display: block !important; + } +} +.visible-print-inline { + display: none !important; +} +@media print { + .visible-print-inline { + display: inline !important; + } +} +.visible-print-inline-block { + display: none !important; +} +@media print { + .visible-print-inline-block { + display: inline-block !important; + } +} +@media print { + .hidden-print { + display: none !important; + } +} +.sandstone { + font-size: 11px; + line-height: 22px; + font-weight: 500; + text-transform: uppercase; +} +.navbar .nav > li > a { + font-size: 11px; + line-height: 22px; + font-weight: 500; + text-transform: uppercase; +} +.navbar-form input, +.navbar-form .form-control { + border: none; +} +.btn { + border: none; + font-size: 11px; + line-height: 22px; + font-weight: 500; + text-transform: uppercase; +} +.btn:hover { + border-color: transparent; +} +.btn-lg { + line-height: 26px; +} +.btn-default:hover { + background-color: #393a35; +} +input, +.form-control { + -webkit-box-shadow: none; + box-shadow: none; +} +/* input:focus, +.form-control:focus { + border-color: #dfd7ca; + -webkit-box-shadow: none; + box-shadow: none; +} */ +.nav { + font-size: 11px; + line-height: 22px; + font-weight: 500; + text-transform: uppercase; +} +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + border-color: #dfd7ca; +} +.nav-tabs > li > a { + background-color: #f8f5f0; + border-color: #dfd7ca; + color: #98978b; +} +.nav-tabs > li.disabled > a:hover { + background-color: #f8f5f0; +} +.nav-pills a { + color: #98978b; +} +.nav-pills li > a { + border: 1px solid transparent; +} +.nav-pills li.active > a, +.nav-pills li > a:hover { + border-color: #dfd7ca; +} +.nav-pills li.disabled > a { + border-color: transparent; +} +.breadcrumb { + font-size: 11px; + line-height: 22px; + font-weight: 500; + text-transform: uppercase; + border: 1px solid #dfd7ca; +} +.breadcrumb a { + color: #98978b; +} +.pagination { + font-size: 11px; + line-height: 22px; + font-weight: 500; + text-transform: uppercase; +} +.pager { + font-size: 11px; + line-height: 22px; + font-weight: 500; + text-transform: uppercase; +} +.pager li > a { + color: #98978b; +} +.dropdown-menu > li > a { + font-size: 11px; + line-height: 22px; + font-weight: 500; + text-transform: uppercase; +} +.alert a, +.alert .alert-link { + color: #fff; +} +.tooltip { + font-size: 11px; + line-height: 22px; + font-weight: 500; + text-transform: uppercase; +} +.progress { + border-radius: 10px; + background-color: #dfd7ca; + -webkit-box-shadow: none; + box-shadow: none; +} +.progress-bar { + -webkit-box-shadow: none; + box-shadow: none; +} +.list-group-item { + padding: 16px 24px; +} +.well { + -webkit-box-shadow: none; + box-shadow: none; +} +.panel { + -webkit-box-shadow: none; + box-shadow: none; +} +.panel .panel-heading, +.panel .panel-title { + font-size: 11px; + line-height: 22px; + font-weight: 500; + text-transform: uppercase; + color: #fff; +} +.panel .panel-footer { + font-size: 11px; + line-height: 22px; + font-weight: 500; + text-transform: uppercase; +} +.panel-default .panel-heading, +.panel-default .panel-title, +.panel-default .panel-footer { + color: #98978b; +} diff --git a/spring-roo/src/main/resources/static/public/fonts/fontawesome-webfont.eot b/spring-roo/src/main/resources/static/public/fonts/fontawesome-webfont.eot new file mode 100644 index 000000000000..99e847f985b5 Binary files /dev/null and b/spring-roo/src/main/resources/static/public/fonts/fontawesome-webfont.eot differ diff --git a/spring-roo/src/main/resources/static/public/fonts/fontawesome-webfont.svg b/spring-roo/src/main/resources/static/public/fonts/fontawesome-webfont.svg new file mode 100644 index 000000000000..5a5f0ecd46fc --- /dev/null +++ b/spring-roo/src/main/resources/static/public/fonts/fontawesome-webfont.svg @@ -0,0 +1,685 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-roo/src/main/resources/static/public/fonts/fontawesome-webfont.ttf b/spring-roo/src/main/resources/static/public/fonts/fontawesome-webfont.ttf new file mode 100644 index 000000000000..f471fde429cc Binary files /dev/null and b/spring-roo/src/main/resources/static/public/fonts/fontawesome-webfont.ttf differ diff --git a/spring-roo/src/main/resources/static/public/fonts/fontawesome-webfont.woff b/spring-roo/src/main/resources/static/public/fonts/fontawesome-webfont.woff new file mode 100644 index 000000000000..646707ac0405 Binary files /dev/null and b/spring-roo/src/main/resources/static/public/fonts/fontawesome-webfont.woff differ diff --git a/spring-roo/src/main/resources/static/public/fonts/fontawesome-webfont.woff2 b/spring-roo/src/main/resources/static/public/fonts/fontawesome-webfont.woff2 new file mode 100644 index 000000000000..aaed3de59c52 Binary files /dev/null and b/spring-roo/src/main/resources/static/public/fonts/fontawesome-webfont.woff2 differ diff --git a/spring-roo/src/main/resources/static/public/fonts/glyphicons-halflings-regular.eot b/spring-roo/src/main/resources/static/public/fonts/glyphicons-halflings-regular.eot new file mode 100644 index 000000000000..17a54a964729 Binary files /dev/null and b/spring-roo/src/main/resources/static/public/fonts/glyphicons-halflings-regular.eot differ diff --git a/spring-roo/src/main/resources/static/public/fonts/glyphicons-halflings-regular.svg b/spring-roo/src/main/resources/static/public/fonts/glyphicons-halflings-regular.svg new file mode 100644 index 000000000000..94fb5490a2ed --- /dev/null +++ b/spring-roo/src/main/resources/static/public/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-roo/src/main/resources/static/public/fonts/glyphicons-halflings-regular.ttf b/spring-roo/src/main/resources/static/public/fonts/glyphicons-halflings-regular.ttf new file mode 100644 index 000000000000..06e2a16b443c Binary files /dev/null and b/spring-roo/src/main/resources/static/public/fonts/glyphicons-halflings-regular.ttf differ diff --git a/spring-roo/src/main/resources/static/public/fonts/glyphicons-halflings-regular.woff b/spring-roo/src/main/resources/static/public/fonts/glyphicons-halflings-regular.woff new file mode 100644 index 000000000000..dda02ad5372b Binary files /dev/null and b/spring-roo/src/main/resources/static/public/fonts/glyphicons-halflings-regular.woff differ diff --git a/spring-roo/src/main/resources/static/public/fonts/glyphicons-halflings-regular.woff2 b/spring-roo/src/main/resources/static/public/fonts/glyphicons-halflings-regular.woff2 new file mode 100644 index 000000000000..ad69b8d4c105 Binary files /dev/null and b/spring-roo/src/main/resources/static/public/fonts/glyphicons-halflings-regular.woff2 differ diff --git a/spring-roo/src/main/resources/static/public/img/apple-touch-icon.png b/spring-roo/src/main/resources/static/public/img/apple-touch-icon.png new file mode 100644 index 000000000000..2a3ac531f2a6 Binary files /dev/null and b/spring-roo/src/main/resources/static/public/img/apple-touch-icon.png differ diff --git a/spring-roo/src/main/resources/static/public/img/en.png b/spring-roo/src/main/resources/static/public/img/en.png new file mode 100644 index 000000000000..ff701e19f6d2 Binary files /dev/null and b/spring-roo/src/main/resources/static/public/img/en.png differ diff --git a/spring-roo/src/main/resources/static/public/img/es.png b/spring-roo/src/main/resources/static/public/img/es.png new file mode 100644 index 000000000000..c2de2d7111e3 Binary files /dev/null and b/spring-roo/src/main/resources/static/public/img/es.png differ diff --git a/spring-roo/src/main/resources/static/public/img/favicon.ico b/spring-roo/src/main/resources/static/public/img/favicon.ico new file mode 100644 index 000000000000..5528ddb9bbd7 Binary files /dev/null and b/spring-roo/src/main/resources/static/public/img/favicon.ico differ diff --git a/spring-roo/src/main/resources/static/public/img/geo.png b/spring-roo/src/main/resources/static/public/img/geo.png new file mode 100644 index 000000000000..a4995b6f56d4 Binary files /dev/null and b/spring-roo/src/main/resources/static/public/img/geo.png differ diff --git a/spring-roo/src/main/resources/static/public/img/logo.png b/spring-roo/src/main/resources/static/public/img/logo.png new file mode 100644 index 000000000000..e23f915763c6 Binary files /dev/null and b/spring-roo/src/main/resources/static/public/img/logo.png differ diff --git a/spring-roo/src/main/resources/static/public/img/owasp_logo.png b/spring-roo/src/main/resources/static/public/img/owasp_logo.png new file mode 100644 index 000000000000..8c820aa076ce Binary files /dev/null and b/spring-roo/src/main/resources/static/public/img/owasp_logo.png differ diff --git a/spring-roo/src/main/resources/static/public/img/springroo-logo.png b/spring-roo/src/main/resources/static/public/img/springroo-logo.png new file mode 100644 index 000000000000..5e9891fc3469 Binary files /dev/null and b/spring-roo/src/main/resources/static/public/img/springroo-logo.png differ diff --git a/spring-roo/src/main/resources/static/public/js/dataTables.advanced.js b/spring-roo/src/main/resources/static/public/js/dataTables.advanced.js new file mode 100644 index 000000000000..ab7f4bc3d729 --- /dev/null +++ b/spring-roo/src/main/resources/static/public/js/dataTables.advanced.js @@ -0,0 +1,1334 @@ +/*! Advanced Configuration for DataTables 1.0.0 + * 2017 DISID Corportation, S.L - www.disid.com + */ +/** + * @summary Advanced configuration and extra features for DataTables + * @description A collection of API methods, events and buttons for DataTables + * that provides advanced features in a DataTable element. Allows developers + * to customize the common Datatables functions easily during the DataTables + * initialization. + * @version 1.0.0 + * @file dataTables.advanced.js + * @author DISID Corporation, S.L (www.disid.com) + * @contact info@disid.com + * @copyright Copyright 2017 DISID Corporation, S.L + * + * This source file is free software, available under the following license: + * Apache 2.0 License - https://www.apache.org/licenses/LICENSE-2.0 + * + * This source file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details. + * + * For details please refer to: https://www.apache.org/licenses/LICENSE-2.0 + */ +(function(factory) { + if (typeof define === 'function' && define.amd) { + // AMD + define(['jquery', 'datatables.net'], function($) { + return factory($, window, document); + }); + } else if (typeof exports === 'object') { + // CommonJS + module.exports = function(root, $) { + if (!root) { + root = window; + } + + if (!$ || !$.fn.dataTable) { + $ = require('datatables.net')(root, $).$; + } + + return factory($, root, root.document); + }; + } else { + // Browser + factory(jQuery, window, document); + } +}(function($, window, document, undefined) { + 'use strict'; + var DataTable = $.fn.dataTable; + + // Version information for debugger + DataTable.advanced = {}; + + DataTable.advanced.version = '1.0.0'; + + DataTable.advanced.init = function(settings) { + + // Getting dt element + var dt = new DataTable.Api(settings); + + // Getting jQueryTable + var $jQueryTable = $(dt.table().node()); + var $jQueryTableBody = $(dt.table().body()); + var $jQueryTableContainer = $(dt.table().container()); + + // Saving default datatables configuration + var dtClasses = $.extend(true, {}, DataTable.ext.classes); + var dtButtons = $.extend(true, {}, DataTable.ext.buttons); + var dtDefaults = $.extend(true, {}, DataTable.defaults); + + // Getting the provided advanced configuration + // and the default options + var init = settings.oInit.advanced; + var opts = $.extend(true, {}, DataTable.defaults.advanced); + + // If developer has customize some function or property, + // override the default ones. + if ($.isPlainObject(init)) { + + if (init.btnsContainerClass !== undefined) { + opts.btnsContainerClass = init.btnsContainerClass; + } + + if (init.buttons !== undefined && $.isPlainObject(init.buttons)) { + $.extend(opts.buttons, init.buttons); + + // If button is provided as null, is necessary to register it + // with the default functionality, but will not be included to be + // displayed. + var hiddenButtons = []; + for (var i in opts.buttons) { + if (opts.buttons.hasOwnProperty(i)) { + var button = opts.buttons[i]; + if (!button) { + opts.buttons[i] = DataTable.defaults.advanced.buttons[i]; + hiddenButtons.push(i); + } + } + } + + } + + if (init.classes !== undefined && $.isPlainObject(init.classes)) { + $.extend(opts.classes, init.classes); + } + + if (init.deferRender !== undefined) { + opts.deferRender = init.deferRender; + } + + if (init.dom !== undefined) { + opts.dom = init.dom; + } + + if (init.loadData !== undefined) { + opts.loadData = init.loadData; + } + + if (init.loadFromState !== undefined) { + opts.loadFromState = init.loadFromState; + } + + if (init.onInitComplete !== undefined) { + opts.onInitComplete = init.onInitComplete; + } + + if (init.processing !== undefined) { + opts.processing = init.processing; + } + + if (init.renderTools !== undefined) { + opts.renderTools = init.renderTools; + } + + if (init.responsive !== undefined) { + opts.responsive = init.responsive; + } + + if (init.retrieve !== undefined) { + opts.retrieve = init.retrieve; + } + + if (init.serverSide !== undefined) { + opts.serverSide = init.serverSide; + } + + if (init.stateSave !== undefined) { + opts.stateSave = init.stateSave; + } + + if (init.defaultButtons !== undefined) { + opts.defaultButtons = init.defaultButtons; + } + + } + + // Configure this Datatables rendering + $.extend(DataTable.ext.classes, opts.classes); + + // Initialize and register buttons + $.extend(DataTable.ext.buttons, opts.buttons); + + // Configure the RenderTools + $.extend(DataTable, { + 'renderTools': opts.renderTools + }); + + // Set datatables defaults + $.extend(DataTable.defaults, { + 'ajax': opts.loadData, + 'buttons': { + 'dom': { + 'container': { + 'className': opts.btnsContainerClass + } + }, + // Obtains a button list from the specified buttons + 'buttons': getButtonsList(opts.buttons, opts.defaultButtons, hiddenButtons) + }, + 'columnDefs': [{ + 'targets': 'checkboxcol', // First column from the left + 'checkboxes': { + 'selectRow': true, + 'selectAllPages': true + } + }, { + 'targets': 'dttools', // First column from the right, + 'width': 100, + 'render': { + 'display': DataTable.renderTools + } + }, { + 'targets': '_all', + 'render': { + 'display': DataTable.render.text() + } + }], + + 'deferRender': opts.deferRender, + 'dom': opts.dom, + 'fnInitComplete': opts.onInitComplete, + 'processing': opts.processing, + 'responsive': opts.responsive, + 'retrieve': opts.retrieve, + 'serverSide': opts.serverSide, + 'stateSave': opts.stateSave, + 'stateSaveParams': opts.loadFromState + }); + + // After extend this datatable functions, is necessary to reinitialize + // the table to apply the new configuration, so is necessary to detroy it + // and draw it again. + // IMPORTANT: To prevent that some event registered by other plugin in the original + // table could be called, is necessary to unregister events. + $jQueryTableBody.off("click"); + // IMPORTANT: You must not specify again the advanced + // attribute during the Datatables initialization to prevent that this process starts again + delete settings.oInit.advanced; + // Destroy the existing table + $jQueryTable.DataTable().destroy(); + // Create the new table using the initial configuration + var datatables = $jQueryTable.DataTable(settings.oInit); + + // Next tables should use the default configuration, so restore it. + DataTable.ext.classes = dtClasses; + DataTable.ext.buttons = dtButtons; + DataTable.defaults = dtDefaults; + + // Finally, register events to this datatable. + registerEvents(datatables); + + }; + + + /* + * Default configuration for the DataTable Advanced + * component. + * + * IMPORTANT: Don't modify this object manually. You should + * configure your DataTables during the initialization. Ex: + * + * $('table').DataTable({ + * advanced : { + * loadData: function(data, callback, settings) { + * // code goes here + * }, + * buttons: { + * // Disable add button + * 'add': null, + * // Customize the delete function + * 'delete': myCustomDeleteFunction + * }, + * stateSave: false + * } + * }); + */ + DataTable.defaults.advanced = { + "btnsContainerClass": 'dt-buttons btn-group col-sm-6', + "buttons": { + 'add': createButton, + 'delete': deleteBatchButton, + 'csv': exportCsvButton, + 'excel': exportExcelButton, + 'pdf': exportPdfButton + }, + "classes": { + "sFilter": "dataTables_filter col-sm-6", + "sInfo": "dataTables_info col-sm-6", + "sPaging": "dataTables_paginate col-sm-6 paging_", + "sProcessing": "dataTables_processing progress-bar progress-bar-striped active" + }, + "defaultButtons": [{ + 'extend': 'colvis', + 'className': 'btn-action', + }, + { + 'extend': 'pageLength', + 'className': 'btn-action', + }], + "deferRender": true, + "dom": 'Bfrtip', + "loadData": loadData, + "loadFromState": loadFromState, + "onInitComplete": onInitComplete, + "processing": true, + "renderTools": renderTools, + "responsive": true, + "retrieve": true, + "serverSide": true, + "stateSave": true + + } + + //////////////////// Private functions ******************************** + + + /** + * This function obtains a button list using the defined buttons + * in the default options or in the custom values provided by the + * developer. + */ + function getButtonsList(buttons, defaultButtons, hiddenButtons) { + // Create an empty array + var buttonsArray = []; + + // Obtain buttons and exclude the hidden buttons + for (var i in buttons) { + if (hiddenButtons === undefined || hiddenButtons.indexOf(i) === -1) { + buttonsArray.push(i); + } + } + + // Finally include default buttons + buttonsArray.push(defaultButtons); + + return buttonsArray; + + } + + + /** + * Creates a new button in the buttons plugin toolbar to create a new row + * using the value of the table tag attribute 'data-create-url-function' as + * a function which returns the URL or, if it is not defined, the value of + * the table tag attribute 'data-create-url' to be used as the URL. + */ + function createButton(datatables, conf) { + var dataCreateUrl = getDataCreateUrl(datatables); + + if (dataCreateUrl) { + /* This code uses a modal dialog to show the creation + * form. This will be the default option in a future + * version. + */ + /* + if (hasParentTable(datatables)) { + return { + 'action': function(e, datatables, node, config) { + if (getParentSelectedRowId(datatables)) { + $('#categoryProductsTableAdd').modal('show'); + } + }, + 'className': 'btn-action add', + 'name': 'add', + 'text': datatables.i18n('buttons.add', 'Add')catego + }; + + } else { + */ + return { + 'action': function(e, datatables, node, config) { + var createUrl = getCreateUrl(datatables); + if (createUrl) { + location.href = createUrl; + } + }, + 'className': 'btn-action add', + 'name': 'add', + 'text': datatables.i18n('buttons.add', 'Add') + }; + //} + } + }; + + /** + * Creates a new button in the buttons plugin toolbar to delete rows selected + * with the checkbox extension, + * using the value of the table tag attribute 'data-delete-url-function' as + * a function which returns the URL or, if it is not defined, the value of + * the table tag attribute 'data-create-url' to be used as the URL. + */ + function deleteBatchButton(datatables, conf) { + var deleteBatchUrl = getDataDeleteBatchUrl(datatables); + + if (deleteBatchUrl) { + return { + 'action': function(e, datatables, node, config) { + var tableId = getTableId(datatables); + var $deleteConfirm = $('#' + tableId + 'DeleteBatchConfirm'); + + // When the delete element modal is opened, copy the current + // element id to be deleted to the 'TABLE_ID + DeleteRowId' + // element + $deleteConfirm.on('show.bs.modal', function(e) { + // Get data-row-id attribute of the clicked element + var rows_selected = datatables.columns().checkboxes.selected(); + + // Populate the row-id data attribute in the modal + $('#' + tableId + 'DeleteBatchRowId').data('row-id', rows_selected.join(",")); + }); + + $('#' + tableId + 'DeleteBatchButton').on('click', function() { + deleteBatchElement(datatables); + }); + + $deleteConfirm.modal(); + }, + 'className': 'btn-action delete', + 'name': 'delete', + 'text': datatables.i18n('buttons.delete', 'Delete') + }; + } + }; + + /** + * Deletes the element whose id is the one in the datatables + * row whose _delete_ button has been selected, and the + * the opened modal confirmacion has been accepted + * (see modal-confirm-delete.html) + * @param datatables DataTable on which the calling should act upon + */ + function deleteBatchElement(datatables) { + var $token = $("meta[name='_csrf']"); + var $header = $("meta[name='_csrf_header']"); + + var tableId = getTableId(datatables); + var rowIds = $('#' + tableId + 'DeleteBatchRowId').data('row-id'); + + var url = getDeleteBatchUrl(datatables, rowIds); + + $.ajax({ + url: url, + type: 'DELETE', + beforeSend: function(request) { + if ($token != null && $token.length > 0 && $header != null && $header.length > 0) { + request.setRequestHeader($header.attr("content"), $token.attr("content")); + } + } + }) + .done(function(result) { + var $deleteSuccess = $('#' + tableId + 'DeleteBatchSuccess'); + $deleteSuccess.modal(); + datatables.columns().checkboxes.deselect(); + datatables.ajax.reload(); // Refresh Datatables + }) + .fail(function(jqXHR, status) { + var $deleteError = $('#' + tableId + 'DeleteBatchError'); + $deleteError.modal(); + }); + } + + /** + * Creates a new button in the buttons plugin toolbar to export data in + * CSV format using the value of the table tag attribute + * 'data-export-csv-url' as a function which returns the value + * to be used as the URL. + */ + function exportCsvButton(datatables, conf) { + var dataExportCsvUrl = getDataValue(datatables, 'export-csv-url'); + if (dataExportCsvUrl) { + return getExportButton(datatables, dataExportCsvUrl, "CSV"); + } + } + + /** + * Creates a new button in the buttons plugin toolbar to export data in + * XLS format using the value of the table tag attribute + * 'data-export-xls-url' as a function which returns the value + * to be used as the URL. + */ + function exportExcelButton(datatables, conf) { + var dataExportXlsUrl = getDataValue(datatables, 'export-xls-url'); + if (dataExportXlsUrl) { + return getExportButton(datatables, dataExportXlsUrl, "XLS"); + } + } + + + /** + * Creates a new button in the buttons plugin toolbar to export data in + * PDF format using the value of the table tag attribute + * 'data-export-pdf-url' as a function which returns the value + * to be used as the URL. + */ + function exportPdfButton(datatables, conf) { + var dataExportPdfUrl = getDataValue(datatables, 'export-pdf-url'); + if (dataExportPdfUrl) { + return getExportButton(datatables, dataExportPdfUrl, "PDF"); + } + } + + /** + * Generates a Datatables export button object using the provided URL + * and the provided type. + * + * @param datatables DataTable on which the calling should act upon + * @param url The url where export button will load + * @param type The type of the export button. Will be used to set + * the button name + * @return a new export button with the provided configuration + */ + function getExportButton(datatables, url, type) { + return { + 'action': function(e, datatables, node, config) { + // Check if current datatable has some records. If not, + // show an error modal and prevent to continue + if (datatables.context[0]._iRecordsDisplay === 0) { + var tableId = getTableId(datatables); + var $exportError = $('#' + tableId + 'ExportEmptyError'); + $exportError.modal(); + return; + } + + // Process the URL to obtain an URL that includes the Datatables + // parameters. + var processedUrl = getUrlWithDatatablesParams(datatables, url); + + // Open the processed URL in a new window + window.open( + processedUrl, + '_blank' + ); + }, + 'className': 'btn-action export-' + type.toLowerCase(), + 'name': type, + 'text': datatables.i18n('buttons.export.' + type.toLowerCase(), type) + }; + } + + /** + * Generates and executes an ajax request whose goal is to load data for a + * DataTable element. + * + * @param data DataTable object data + * @param callback Name of the function to call with the server data obtained + * once the ajax request has been completed + * @param settings DataTable object options + */ + function loadData(data, callback, settings) { + var datatables = this.DataTable(); + var url = getLoadUrl(datatables); + if (url) { + loadDataFromUrl(datatables, data, callback, url); + } else { + callback(emptyData(data.draw)); + } + } + + /** + * Generates and executes an ajax request whose goal is to load data for a + * DataTable element. + * + * @param datatables DataTable on which the calling should act upon + * @param data DataTable object data + * @param callback Name of the function to call with the server data obtained + * once the ajax request has been completed + * @param url Url to use for ajax request + */ + function loadDataFromUrl(datatables, data, callback, url) { + var $token = $("meta[name='_csrf']"); + var $header = $("meta[name='_csrf_header']"); + + var prefix = 'loadUrlParam'; + var dataAttrs = getAllDataValues(datatables); + $.each(dataAttrs, function(property, value) { + if (property.length > prefix.length && property.lastIndexOf(prefix, 0) === 0) { + var param = toLowerCaseFirst(property.substring(prefix.length)); + data[param] = value; + } + }); + + $.ajax({ + url: url, + type: 'GET', + data: data, + dataType: 'json', + headers: { + Accept: "application/vnd.datatables+json", + }, + context: datatables, + beforeSend: function(request) { + if ($token != null && $token.length > 0 && $header != null && $header.length > 0) { + request.setRequestHeader($header.attr("content"), $token.attr("content")); + } + } + }) + .done(function(result) { + callback(result); + if (datatables.state.loaded()) { + var rowSelectedId = datatables.state.loaded().rowSelectedId; + if (rowSelectedId) { + var rowSelected = datatables.row('#' + rowSelectedId); + if (rowSelected.length > 0) { + rowSelected.select(); + } + } + } + }) + .fail(function(jqXHR, status) { + if (jqXHR.responseJSON != null && jqXHR.responseJSON.status === 403) { + var settings = this.settings()[0]; + settings.oLanguage.sEmptyTable = "

Your session has expired or you have insufficient permissions

" + + "Refresh"; + } + callback(emptyData(data.draw)); + }); + } + + + /** + * Converts the first char of the given string to lower case. + * @param str the string to convert + * @returns the converted string + */ + function toLowerCaseFirst(str) { + if (str.length > 0) { + var value = str.charAt(0).toLowerCase(); + if (str.length > 1) { + value = value.concat(str.slice(1)); + } + return value; + } + } + + /** + * Returns the parent datatables whose id is given through + * the 'data-parent-table' attribute. + */ + function getParentDatatables(datatables) { + var parentTableId = getParentTableId(datatables); + // Validate if parent-table id has been specified and + // if the parent Datatable has been initialized + if (parentTableId && $.fn.dataTable.isDataTable($(parentTableId))) { + return $(parentTableId).DataTable(); + } + } + + /** + * Returns the parent table id when this datatables is a detail. + */ + function getParentTableId(datatables) { + var parentTableId = getDataValue(datatables, 'parent-table'); + if (parentTableId) { + return "#" + parentTableId; + } + } + + /** + * Returns the id of the selected row in the parent table, if any. + * @param datatables child datatables + * @returns the id of the parent datatables selected row + */ + function getParentSelectedRowId(datatables) { + var parentDatatables = getParentDatatables(datatables); + if (parentDatatables) { + var selected = parentDatatables.row({ + selected: true + }); + + if (selected.any()) { + return selected.data().id; + } + } + } + + /** + * Returns if the datatables has a related parent datatables + * @param datatables to find if it has a parent datatables + * @returns if there is a parent datatables + */ + function hasParentTable(datatables) { + var parentTableId = getParentTableId(datatables); + if (parentTableId) { + return true; + } + return false; + } + + /** + * Process the given Url to perform the following actions* + * - If the given datatables is not related to a parent one, it + * returns the given url as is. + * - If the url contains the '_PARENTID_' valuea and there is a related + * parent table, if the parent table has a selected row, its identifier + * is used to replace the '_PARENTID_' value in the given url. + * Otherwise no url is returned because it is considered as an invalid + * url. + * If the processed url is valid, the given id value is used to replace + * the '_ID_' parameter in the url + * @param datatables DataTable on which the calling should act upon + * @param url to process + * @param id (optional) identifier of the datatables row to act upon + * @returns the processed url + */ + function processUrl(datatables, url, id) { + var processedUrl = url; + // If it is a detail table, we have to get the parent id from + // the selected row in the parent table, and replace the + // _PARENTID_ variable in the given URL. + if (url && url.indexOf('_PARENTID_') > -1 && hasParentTable(datatables)) { + var parentRowId = getParentSelectedRowId(datatables); + if (parentRowId !== undefined) { + processedUrl = url.replace('_PARENTID_', parentRowId); + } else { + processedUrl = undefined; + } + } + + if (id !== undefined && processedUrl) { + processedUrl = processedUrl.replace('_ID_', id); + } + + return processedUrl; + } + + /** + * Process the given Url and the Datatables configuration to build + * an URL that contains the Datatables parameters. + * This function is useful when is necessary to make a petition + * to the server side without using AJAX. + * + * @param datatables DataTable on which the calling should act upon + * @param url to process + * @returns the processed url + */ + function getUrlWithDatatablesParams(datatables, url) { + + // Remove existing parameters + url = url.split("?")[0]; + + // Getting data from Datatables + var dtContext = datatables.context[0]; + var data = dtContext.oAjaxData; + + // Getting search value + var searchValue = data.search.value; + + // Getting order + var order = data.order; + + // Getting columns + var columns = data.columns; + + var sortParams = ""; + for (var i = 0; i < order.length; i++) { + if (order[i] !== null && order[i] !== undefined && + order[i].column !== null && order[i].column !== undefined) { + var columnName = columns[order[i].column].data; + var dir = order[i].dir; + + sortParams += "sort=" + columnName + "," + dir + "&"; + + } + } + if (sortParams.length > 0) { + sortParams = sortParams.substr(0, sortParams.length - 1); + } + + var datatablesColumns = ""; + for (i = 0; i < columns.length; i++) { + if (columns[i] !== null && columns[i] !== undefined && + columns[i].data !== null && columns[i].data !== undefined && + datatablesColumns.indexOf(columns[i].data) === -1) { + datatablesColumns += columns[i].data + ","; + } + } + if (datatablesColumns.length > 0) { + datatablesColumns = datatablesColumns.substr(0, datatablesColumns.length - 1); + datatablesColumns = "datatablesColumns=" + datatablesColumns; + } + + + // Build URL parameters + var hasParameters = false; + var params = ""; + if (searchValue != null && searchValue != "" && searchValue != undefined) { + if (hasParameters) { + params += "&"; + } + params += "search[value]=" + searchValue; + hasParameters = true; + } + + if (sortParams != null && sortParams != "" && sortParams != undefined) { + if (hasParameters) { + params += "&"; + } + params += sortParams; + hasParameters = true; + } + + if (datatablesColumns != null && datatablesColumns != "" && datatablesColumns != undefined) { + if (hasParameters) { + params += "&"; + } + params += datatablesColumns; + } + + return url + "?" + params; + + } + + /** + * Deletes the element whose id is the one in the datatables + * row whose _delete_ button has been selected, and the + * the opened modal confirmacion has been accepted + * (see modal-confirm-delete.html) + * @param datatables DataTable on which the calling should act upon + */ + function deleteElement(datatables) { + var $token = $("meta[name='_csrf']"); + var $header = $("meta[name='_csrf_header']"); + + var tableId = getTableId(datatables); + var rowId = $('#' + tableId + 'DeleteRowId').data('row-id'); + var url = getDeleteUrl(datatables, rowId); + + $.ajax({ + url: url, + type: 'DELETE', + beforeSend: function(request) { + if ($token != null && $token.length > 0 && $header != null && $header.length > 0) { + request.setRequestHeader($header.attr("content"), $token.attr("content")); + } + } + }) + .done(function(result) { + var $deleteSuccess = $('#' + tableId + 'DeleteSuccess'); + $deleteSuccess.modal(); + datatables.ajax.reload(); // Refresh Datatables + }) + .fail(function(jqXHR, status) { + var $deleteError = $('#' + tableId + 'DeleteError'); + $deleteError.modal(); + }); + } + + /** + * Returns the URL to load the data for a Datatables. The value + * is defined through a 'data-load-url' attribute in the + * Datatables table tag. + * @param datatables DataTable on which the calling should act upon + */ + function getLoadUrl(datatables) { + var url = getDataValue(datatables, 'load-url'); + return processUrl(datatables, url); + } + + /** + * Returns the URL to create a new element for the Datatables. + * The URL is processed to replace any parameters. + * + * @param datatables DataTable on which the calling should act upon + */ + function getCreateUrl(datatables) { + var url = getDataCreateUrl(datatables); + return processUrl(datatables, url); + } + + /** + * Returns the URL to create a new element for the Datatables + * as defined in the table data attributes. + * The value is defined in the Datatables table tag with a + * 'data-create-url-function' as a function which returns the URL + * or, if it is not defined, the value of the attribute + * 'data-create-url' to be used as the URL. + * + * @param datatables DataTable on which the calling should act upon + */ + function getDataCreateUrl(datatables) { + var urlFunction = getDataValue(datatables, 'create-url-function'); + var url = urlFunction ? $[urlFunction]() : getDataValue(datatables, 'create-url'); + return url; + } + + /** + * Returns the URL to show the details of an element of the Datatables. + * The value is defined in the Datatables table tag with a + * 'data-show-url' as the URL to use. + * The URL contains the text *_ID_* in the place where + * the selected element Id has to be inserted. + * + * @param datatables DataTable on which the calling should act upon + * @param id identifier of the element to edit + */ + function getShowUrl(datatables, id) { + if (id == null || id === "null") { + return null; + } + var url = getDataValue(datatables, 'show-url'); + return processUrl(datatables, url, id); + } + + /** + * Returns the URL to edit an element of the Datatables. + * The value is defined in the Datatables table tag with a + * 'data-edit-url' as the URL to use. + * The URL contains the text *_ID_* in the place where + * the selected element Id has to be inserted. + * + * @param datatables DataTable on which the calling should act upon + * @param id identifier of the element to edit + */ + function getEditUrl(datatables, id) { + var url = getDataValue(datatables, 'edit-url'); + return processUrl(datatables, url, id); + } + + /** + * Returns the URL to remove an element of the Datatables. + * The value is defined in the Datatables table tag with a + * 'data-delete-url' as the URL to use. + * The URL contains the text *_ID_* in the place where + * the selected element Id has to be inserted. + * + * @param datatables DataTable on which the calling should act upon + */ + function getDataDeleteUrl(datatables) { + var url = getDataValue(datatables, 'delete-url'); + return url; + } + + /** + * Returns the URL to remove an element of the Datatables. + * The value is defined in the Datatables table tag with a + * 'data-delete-url' as the URL to use. + * + * The URL contains the text *_ID_* in the place where + * the selected element Id has to be inserted, AND it is + * replaced with the provided id. + * + * @param datatables DataTable on which the calling should act upon + * @param id identifier of the element to remove + */ + function getDeleteUrl(datatables, id) { + var url = getDataDeleteUrl(datatables); + return processUrl(datatables, url, id); + } + + /** + * Returns the URL to remove a list of elements from the Datatables. + * The value is defined in the Datatables table tag with a + * 'data-delete-batch-url' as the URL to use. + * The URL contains the text *_ID_* in the place where + * the selected elements Ids have to be inserted. + * + * @param datatables DataTable on which the calling should act upon + */ + function getDataDeleteBatchUrl(datatables) { + var url = getDataValue(datatables, 'delete-batch-url'); + return url; + } + + /** + * Returns the URL to remove a list of elements from the Datatables. + * The value is defined in the Datatables table tag with a + * 'data-delete-batch-url' as the URL to use. + * The URL contains the text *_ID_* in the place where + * the selected elements Ids have to be inserted, and it is + * replaced with the provided idlist. + * + * @param datatables DataTable on which the calling should act upon + * @param idlist list of identifiers of the elements to remove + */ + function getDeleteBatchUrl(datatables, idlist) { + var url = getDataDeleteBatchUrl(datatables); + return processUrl(datatables, url, idlist); + } + + /** + * Returns the 'data-name' attribute value of a datatables. + * @param datatables DataTable on which the calling should act upon + * @param name the name of the data attribute to return the value of + */ + function getDataValue(datatables, name) { + var $dt = jQueryTable(datatables); + return $dt.data(name); + } + + /** + * Returns all the 'data-*' attributes of a datatables. + * @param datatables DataTable on which the calling should act upon + */ + function getAllDataValues(datatables) { + var $dt = jQueryTable(datatables); + return $dt.data(); + } + + /** + * Returns the jQuery object for the given datatables element. + */ + function jQueryTable(datatables) { + return $(datatables.table().node()); + } + + /** + * Returns the table id attribute for the given datatables element. + */ + function getTableId(datatables) { + var $jQueryTable = jQueryTable(datatables); + return $jQueryTable.attr('id'); + } + + /** + * Generates a JSON object with the necessary data for indicating a + * DataTables object that 0 elements have been found. + * This is used for details related tables, when a parent table + * row is not selected. + * + * @param draw DataTables request counter + * @returns {json} JSON object with empty data + */ + function emptyData(draw) { + return { + 'data': [], + 'draw': draw, + 'error': null, + 'recordsFiltered': '0', + 'recordsTotal': '0' + }; + } + + /** + * This function will be called when DataTables has been fully + * initialised and data loaded. + */ + function onInitComplete(oSettings, json) { + var datatables = this.DataTable(); + // Save the selected row to state + saveSelectedRowToState(datatables, oSettings, json); + // Register checkboxes + registerCheckBoxesEvents(datatables); + } + + /** + * If a row is selected, store it in the persisted table state + * so if the user goes to another page and returns, the current + * selected row is still selected. + * @param datatables the Datatables element + * @param oSettings DataTable object options + * @param json + */ + function saveSelectedRowToState(datatables, oSettings, json) { + var state = datatables.state; + datatables.on('select', function(e, dt, type, indexes) { + if (type === 'row') { + var rowSelectedId = datatables.rows(indexes).ids()[0]; + state.loaded().rowSelectedId = rowSelectedId; + state.save(); + } + }); + datatables.on('deselect', function(e, dt, type, indexes) { + if (type === 'row') { + state.loaded().rowSelectedId = undefined; + state.save(); + } + }); + if (!state.loaded()) { + oSettings.oLoadedState = datatables.state(); + } + } + + /** + * Loads a previously persisted datatables state. + * @param settings DataTable object options + * @param data DataTable object data + */ + function loadFromState(settings, data) { + var datatables = this.DataTable(); + loadSelectedRowFromState(datatables, data); + } + + /** + * Loads a previously selected row id from the persisted state. + * @param settings DataTable object options + * @param data DataTable object data + */ + function loadSelectedRowFromState(datatables, data) { + var state = datatables.state; + if (state.loaded()) { + var rowSelectedId = state.loaded().rowSelectedId; + if (rowSelectedId) { + data.rowSelectedId = rowSelectedId; + } + } + } + + /** + * Registers events for the given datatables + */ + function registerEvents(datatables) { + registerDeleteModalEvents(datatables); + registerAddModalEvents(datatables); + registerToParentEvents(datatables); + registerOnDrawFinishesEvents(datatables); + } + + /** + * This function registers all the necessary actions to execute + * when the provided datatables is completly drawed. + */ + function registerOnDrawFinishesEvents(datatables) { + // When this datatable is re-drawed, the + // following actions will be executed + datatables.on('draw.dt', function() { + // Register events to the new included checkboxes + registerCheckBoxesEvents(datatables); + }); + } + + /** + * Registers events related to the checkboxes of the given + * datatables. + */ + function registerCheckBoxesEvents(datatables) { + // Getting the table id + var tableId = getTableId(datatables); + // Obtain all checkboxes for this table + var checkBoxes = jQuery("#" + tableId + " input:checkbox"); + // Register change event for every checkbox. Every time that some checkbox + // changes, validates if the delete batch button should be enabled or not + jQuery.each(checkBoxes, function(item) { + jQuery(this).change(function() { + var rows_selected = datatables.columns().checkboxes.selected(); + if (rows_selected.join(",") === "") { + datatables.button('delete:name').disable(); + } else { + datatables.button('delete:name').enable(); + } + }); + }); + + // Re-initialize the delete batch button + var rows_selected = datatables.columns().checkboxes.selected(); + if (rows_selected.join(",") === "") { + datatables.button('delete:name').disable(); + } else { + datatables.button('delete:name').enable(); + } + + } + + /** + * Registers the events related to the delete modals, so the + * modal knows the id of the row to delete. + */ + function registerDeleteModalEvents(datatables) { + var tableId = getTableId(datatables); + var $deleteConfirm = $('#' + tableId + 'DeleteConfirm'); + + // When the delete element modal is opened, copy the current + // element id to be deleted to the 'TABLE_ID + DeleteRowId' + // element + $deleteConfirm.on('show.bs.modal', function(e) { + // Get data-row-id attribute of the clicked element + var rowId = jQuery(e.relatedTarget).data('row-id'); + // Populate the row-id data attribute in the modal + $('#' + tableId + 'DeleteRowId').data('row-id', rowId) + }); + + $('#' + tableId + 'DeleteButton').on('click', function() { + deleteElement(datatables); + }); + } + + /** + * When a table is linked to parent table, for a master detail list + * for example, it registers the row selection events in the parent + * table to update the data in the child table. + */ + function registerToParentEvents(datatables) { + var parentDatatables = getParentDatatables(datatables); + + if (parentDatatables) { + // Register to de/select events + parentDatatables.on('select', function() { + datatables.button('add:name').enable(); + datatables.ajax.reload(); + }); + + parentDatatables.on('deselect', function() { + datatables.button('add:name').disable(); + datatables.ajax.reload(); + }); + + // Register to reload finished event, needed when the selected row has + // been deleted in the parent table or any other change + parentDatatables.on('xhr.dt', function() { + datatables.ajax.reload(); + }); + + datatables.button('add:name').disable(); + } + } + + /** + * Registers the events related to the delete modals, so the + * modal knows the id of the row to delete. + */ + function registerAddModalEvents(datatables) { + var parentDatatables = getParentDatatables(datatables); + + // The add modal dialog is only used in child datatables + if (parentDatatables) { + var tableId = getTableId(datatables); + + $('#' + tableId + 'AddButton').on('click', function() { + var url = getCreateUrl(datatables); + $addForm = $('#' + tableId + 'AddForm'); + var params = $addForm.serialize(); + $.ajax({ + type: $addForm.attr('method'), + url: url, + data: params, + success: function(data) { + datatables.ajax.reload(); + } + }); + }); + } + } + + /** + * Renders the tools column, with the buttons to perform operations + * on the table rows. + */ + function renderTools(data, type, full, meta) { + var datatables = new $.fn.dataTable.Api(meta.settings); + var tableId = getTableId(datatables); + var rowId = data; + var buttons = '
'; + + var showUrl = getShowUrl(datatables, rowId); + // Check if the show will be inline + var showInline = getDataValue(datatables, 'show-inline'); + if (showUrl && !showInline) { + buttons = buttons.concat(''); + }else if(showUrl && showInline){ + buttons = buttons.concat(''); + } + + var editUrl = getEditUrl(datatables, rowId); + if (editUrl) { + buttons = buttons.concat(''); + } + + var deleteUrl = getDeleteUrl(datatables, rowId); + if (deleteUrl) { + buttons = buttons.concat(''); + } + + buttons = buttons.concat('
'); + return buttons; + } + + + /** + * This method tries to display the show view of the selected record + * expanding the selected row. + */ + function showInline(showButton, datatables, showUrl){ + var tr = showButton.closest('tr'); + var row = datatables.row( tr ); + if ( row.child.isShown() ) { + // This row is already open - close it + $(showButton).attr("aria-expanded", "false"); + row.child.hide(); + } + else { + $(showButton).attr("aria-expanded", "true"); + $.ajax({ + url: showUrl + "/inline", + dataType: 'html' + }).done(function(data) { + // Open this row + row.child(data).show(); + }).fail(function(data){ + // Show error in new row + row.child("
ERROR: An error occurred while trying to obtain more info.
").show(); + }); + } + } + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * DataTables Advanced Extension API + * + * For complete documentation, please refer to the docs/api directory or the + * DataTables site + */ + + // Local variables to improve compression + var apiRegister = DataTable.Api.register; + + apiRegister('advanced()', function() { + return this.iterator('table', function(settings) { + DataTable.advanced.init(settings); + }); + }); + + apiRegister('advanced.getTableId()', getTableId); + apiRegister('advanced.getCreateUrl()', getCreateUrl); + apiRegister('advanced.getEditUrl()', getEditUrl) + apiRegister('advanced.getDeleteUrl()', getDeleteUrl); + apiRegister('advanced.getDeleteBatchUrl()', getDeleteBatchUrl); + apiRegister('advanced.getShowUrl()', getShowUrl); + apiRegister('advanced.getDataValue()', getDataValue); + apiRegister('advanced.processUrl()', processUrl); + + apiRegister('advanced.getCreateButton()', createButton); + apiRegister('advanced.getDeleteBatchButton()', deleteBatchButton); + apiRegister('advanced.getExportCsvButton()', exportCsvButton); + apiRegister('advanced.getExportExcelButton()', exportExcelButton); + apiRegister('advanced.getExportPdfButton()', exportPdfButton); + + apiRegister('advanced.showInline()', showInline); + + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Initialization + */ + + // DataTables creation - check if advanced has been defined in the options. + $(document).on('preInit.dt.dtadvanced', function(e, settings) { + if (e.namespace !== 'dt') { + return; + } + + // If the 'advanced' option has been specified, + // initialize the advanced configuration for this + // DataTable. + if (settings.oInit.advanced !== undefined) { + DataTable.advanced.init(settings); + } + + }); + + return DataTable.advanced; +})); diff --git a/spring-roo/src/main/resources/static/public/js/datatables-defaults.js b/spring-roo/src/main/resources/static/public/js/datatables-defaults.js new file mode 100644 index 000000000000..ab247078e41c --- /dev/null +++ b/spring-roo/src/main/resources/static/public/js/datatables-defaults.js @@ -0,0 +1,29 @@ +// IIFE - Immediately Invoked Function Expression +(function(extendDatatables) { + + // The global jQuery object is passed as a parameter + extendDatatables(window.jQuery, window, document); + +}(function($, window, document) { + + // The $ is now locally scoped, it won't collide with other libraries + + // Listen for the jQuery ready event on the document + // READY EVENT BEGIN + $(function() { + // Initialize all datatables in current page + $('table[data-datatables="true"]').each(function(){ + // Use the advanced extension to auto-configure all + // advanced features (ajax, export, add, edit, show, delete, etc.) + $(this).DataTable({ + mark: true, + advanced: true + }); + }); + }); + + // READY EVENT END + //console.log('The DOM may not be ready'); + + // The rest of code goes here! +})); \ No newline at end of file diff --git a/spring-roo/src/main/resources/static/public/js/datetimepicker-defaults.js b/spring-roo/src/main/resources/static/public/js/datetimepicker-defaults.js new file mode 100644 index 000000000000..3d114d93aaec --- /dev/null +++ b/spring-roo/src/main/resources/static/public/js/datetimepicker-defaults.js @@ -0,0 +1,107 @@ +(function(jQuery) { + jQuery(document).ready(function() { + + /* + * jQuery Utilities ================================================== + */ + /** + * Select the most switchable time format for time selectod related to + * requiered format + * + * @param format + * @returns time format + */ + function getSelectorTimeFormat(format) { + // + if (format.search(/h{1,2}/) > -1 && format.search(/[aA]/) > -1) { + if (format.search(/[A]/) > -1) { + return "hh:mm A"; + } else { + return "hh:mm a"; + } + } + return "HH:mm"; + } + + // Use the same locale than MomentJs + // (set it before setDateFormater as setLocale override formatter) + jQuery.datetimepicker.setLocale(moment.locale()); + + // Define parse/format date using moment library + jQuery.datetimepicker.setDateFormatter({ + parseDate : function(date, format) { + var d = moment(date, format); + return d.isValid() ? d.toDate() : false; + }, + + formatDate : function(date, format) { + return moment(date).format(format); + } + }); + + jQuery(".datetimepicker").each(function(index) { + var $input = jQuery(this); + var options = { + step : 5 + }; + + var pattern = $input.attr("data-dateformat"); + var value = $input.attr("data-timestep"); + + if (value) { + try { + options.step = parseInt(value); + } catch (e) { + timeStep = 5; + } + } + + // FormatDate YYYY/MM/DD + value = $input.attr("data-startdate"); + if (value) { + options.startDate = value; + } + value = $input.attr("data-mindate"); + if (value) { + options.minDate = value; + } + value = $input.attr("data-maxdate"); + if (value) { + options.maxDate = value; + } + + // FormatTime : "HH:mm" + value = $input.attr("data-mintime"); + if (value) { + options.minTime = value; + } + value = $input.attr("data-maxtime"); + if (value) { + options.maxTime = value; + } + value = $input.attr("data-allowtimes"); + if (value) { + options.allowTimes = value.split(","); + } + + if (isNotEmpty(pattern)) { + var momentPattern = moment.javaToMomentDateFormat(pattern); + jQuery.extend(options, { + format : momentPattern, + datepicker : moment.isDateFormatDate(momentPattern), + timepicker : moment.isDateFormatTime(momentPattern), + formatDate : "YYYY/MM/DD", + formatTime : getSelectorTimeFormat(momentPattern) + }); + } else { + var emptyMomentPattern = moment.javaToMomentDateFormat(); + jQuery.extend(options, { + format : emptyMomentPattern, + formatDate : "YYYY/MM/DD", + formatTime : "HH:mm" + }); + } + $input.datetimepicker(options); + }); + }); +})(jQuery); \ No newline at end of file diff --git a/spring-roo/src/main/resources/static/public/js/inputmask-defaults.js b/spring-roo/src/main/resources/static/public/js/inputmask-defaults.js new file mode 100644 index 000000000000..f3a299bb5d9f --- /dev/null +++ b/spring-roo/src/main/resources/static/public/js/inputmask-defaults.js @@ -0,0 +1,30 @@ +(function(jQuery) { + jQuery(document).ready(function() { + + /* + Inputmask.extendDefaults({ + 'autoUnmask' : true + }); + */ + + jQuery(".inputmask").each(function(index) { + var $input = jQuery(this); + var options = { + removeMaskOnSubmit : true + }; + + var pattern = $input.attr("data-inputmask-mask"); + if (pattern) { + options.mask = pattern; + } else { + var alias = $input.attr("data-inputmask-alias"); + if (alias) { + options.alias = alias; + } else { + throw "missing input initialization value on (id='"+this.id+"' name='"+this.name+"')"; + } + } + $input.inputmask(options); + }); + }); +})(jQuery); \ No newline at end of file diff --git a/spring-roo/src/main/resources/static/public/js/main.js b/spring-roo/src/main/resources/static/public/js/main.js new file mode 100644 index 000000000000..b704282624d7 --- /dev/null +++ b/spring-roo/src/main/resources/static/public/js/main.js @@ -0,0 +1,70 @@ +(function(jQuery) { + jQuery(document).ready( + function() { + + //dropdown + jQuery('ul.dropdown-menu [data-toggle=dropdown]').on( + 'click', + function(event) { + event.preventDefault(); + event.stopPropagation(); + jQuery(this).parent().siblings() + .removeClass('open'); + jQuery(this).parent().toggleClass('open'); + }); + + //tooltip + jQuery('[data-toggle="tooltip"]').tooltip(); + + }); +})(jQuery); + +/** + * Checks if an object in JavaScript is undefined, null or empty string + * + * @param obj + * @returns {Boolean} + */ +function isEmpty(obj) { + if (jQuery.isPlainObject(obj)) { + return jQuery.isEmptyObject(obj); + } else if (jQuery.isArray(obj)) { + return 0 === obj.length; + } else if (typeof obj === "string") { + return (isNull(obj) || 0 === obj.length); + } + return isNull(obj); +} + +/** + * Checks if an object in JavaScript is undefined or null + * + * @param obj + * @returns {Boolean} + */ +function isNull(obj) { + if (typeof obj === "undefined" || obj == null) { + return true; + } + return false; +} + +/** + * Checks if an object in JavaScript is defined and not null + * + * @param obj + * @returns {Boolean} + */ +function isNotNull(obj) { + return !isNull(obj); +} + +/** + * Checks if an object in JavaScript is undefined, null or empty string + * + * @param obj + * @returns {Boolean} + */ +function isNotEmpty(obj) { + return !isEmpty(obj); +} diff --git a/spring-roo/src/main/resources/static/public/js/moment-defaults.js b/spring-roo/src/main/resources/static/public/js/moment-defaults.js new file mode 100644 index 000000000000..e8f4cf963a6d --- /dev/null +++ b/spring-roo/src/main/resources/static/public/js/moment-defaults.js @@ -0,0 +1,88 @@ +(function(jQuery) { + + moment().format(); + + /** + * Convert Java's SimpleDateFormat to momentJS formatDate. Takes a Java + * pattern + * (http://docs.oracle.com/javase/6/docs/api/java/text/SimpleDateFormat.html) + * and turns it into the expected momentJS formatDate + * (http://momentjs.com/docs/#/parsing/string-format/). + * + * @param pattern + * SimpleDateFormat pattern + * @return moment pattern (if 'pattern' is ommited return defautl + * pattern) + */ + moment.javaToMomentDateFormat = function (pattern) { + if (pattern) { + // Year + if (pattern.search(/y{3,}/g) >= 0) { + pattern = pattern.replace(/y{3,}/g, "YYYY"); // yyyy to + // yy + } else if (pattern.search(/y{2}/g) >= 0) { // yy to YY + pattern = pattern.replace(/y{2}/g, "YY"); + } + + // Day + if (pattern.search(/d{2,}/g) >= 0) { // dd to DD + pattern = pattern.replace(/d{2,}/g, "DD"); + } else if (pattern.search(/d{1}/g) >= 0) { // d to D + pattern = pattern.replace(/d{1}/g, "D"); + } else if (pattern.search(/D{1,}/g) >= 0) { // D,DD, DDD to DDD + pattern = pattern.replace(/D{1,}/g, "DDD"); + } + + // Day in week + if (pattern.search(/E{4,}/g) >= 0) { // EEEE to dddd + pattern = pattern.replace(/E{4,}/g, "dddd"); + } else if (pattern.search(/E{2,3}/g) >= 0) { // EEE to ddd + pattern = pattern.replace(/E{2,3}/g, "ddd"); + } + + // Day in week (number) + if (pattern.search(/F{1}/g) >= 0) { // F to e + pattern = pattern.replace(/F{1}/g, "e"); + } + + // week of the year + if (pattern.search(/w{1,}/g) >= 0) { // ww to WW + pattern = pattern.replace(/w{1,}/g, "WW"); + } + } else { + return "YYYY/MM/DD HH:mm"; + } + + return pattern; + } + + /** + * Informs if date format (momentJS) includes date information + * + * @param format + * string + * @returns true if !format or format contains ('YQDMdw') + */ + moment.isDateFormatDate = function (format) { + if (!format) { + return true; + } + return format.search(/[YQDMdw]/) > -1; + } + + /** + * Informs if date format (ISO 8601) includes time information + * + * @param format + * string + * @returns true if !format or format contains ('HmAasSZ') + */ + moment.isDateFormatTime = function (format) { + if (!format) { + return true; + } + return format.search(/[HhmAasSZ]/) > -1; + ; + } + +})(jQuery); diff --git a/spring-roo/src/main/resources/static/public/js/moment-locale-es.js b/spring-roo/src/main/resources/static/public/js/moment-locale-es.js new file mode 100644 index 000000000000..fbf94e406e49 --- /dev/null +++ b/spring-roo/src/main/resources/static/public/js/moment-locale-es.js @@ -0,0 +1,73 @@ +//! moment.js locale configuration +//! locale : spanish (es) +//! author : Julio Napurí : https://github.com/julionc + +// ROO-3814 Remove var "monthsShortDot" and "if" that assigns it. + +;(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' + && typeof require === 'function' ? factory(require('../moment')) : + typeof define === 'function' && define.amd ? define(['moment'], factory) : + factory(global.moment) +}(this, function (moment) { 'use strict'; + + var es = moment.defineLocale('es', { + months : 'enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre'.split('_'), + monthsShort : 'ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic'.split('_'), + monthsParseExact : true, + weekdays : 'domingo_lunes_martes_miércoles_jueves_viernes_sábado'.split('_'), + weekdaysShort : 'dom._lun._mar._mié._jue._vie._sáb.'.split('_'), + weekdaysMin : 'do_lu_ma_mi_ju_vi_sá'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'H:mm', + LTS : 'H:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D [de] MMMM [de] YYYY', + LLL : 'D [de] MMMM [de] YYYY H:mm', + LLLL : 'dddd, D [de] MMMM [de] YYYY H:mm' + }, + calendar : { + sameDay : function () { + return '[hoy a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; + }, + nextDay : function () { + return '[mañana a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; + }, + nextWeek : function () { + return 'dddd [a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; + }, + lastDay : function () { + return '[ayer a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; + }, + lastWeek : function () { + return '[el] dddd [pasado a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; + }, + sameElse : 'L' + }, + relativeTime : { + future : 'en %s', + past : 'hace %s', + s : 'unos segundos', + m : 'un minuto', + mm : '%d minutos', + h : 'una hora', + hh : '%d horas', + d : 'un día', + dd : '%d días', + M : 'un mes', + MM : '%d meses', + y : 'un año', + yy : '%d años' + }, + ordinalParse : /\d{1,2}º/, + ordinal : '%dº', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } + }); + + return es; + +})); \ No newline at end of file diff --git a/spring-roo/src/main/resources/static/public/js/select2-defaults.js b/spring-roo/src/main/resources/static/public/js/select2-defaults.js new file mode 100644 index 000000000000..6e45799dfbb1 --- /dev/null +++ b/spring-roo/src/main/resources/static/public/js/select2-defaults.js @@ -0,0 +1,49 @@ +// IIFE - Immediately Invoked Function Expression +(function(extendSelect2) { + + // The global jQuery object is passed as a parameter + extendSelect2(window.jQuery, window, document); + +}(function($, window, document) { + + // The $ is now locally scoped, it won't collide with other libraries + + // Listen for the jQuery ready event on the document + // READY EVENT BEGIN + $(function() { + + // The DOM is ready! + //console.log('The DOM is ready'); + + // Init select simple + $('.dropdown-select-simple').select2({ + debug : false, + theme : 'bootstrap', + allowClear : true, + }); + + // Init select with AJAX search + $('.dropdown-select-ajax').select2({ + debug : false, + theme : 'bootstrap', + allowClear : true, + ajax : { + data : function(params) { + // set search params names to match with GlobalSearch and + // Pageable arguments + var query = { + 'search[value]' : params.term, + 'page' : params.page - 1, + }; + return query; + } + } + }); + }); + + // READY EVENT END + //console.log('The DOM may not be ready'); + + // The rest of code goes here! +})); + diff --git a/spring-roo/src/main/resources/static/public/js/validation-defaults.js b/spring-roo/src/main/resources/static/public/js/validation-defaults.js new file mode 100644 index 000000000000..a92da83d5867 --- /dev/null +++ b/spring-roo/src/main/resources/static/public/js/validation-defaults.js @@ -0,0 +1,233 @@ +(function(jQuery) { + jQuery(document) + .ready( + function() { + + function getLanguage() { + // usamos el locale de momentjs + moment.locale(); + } + + jQuery.validator.setDefaults({ + ignoreTitle : true + }); + + /** + * Initialize jQuery Validator methods + */ + /** + * Date/time validation with format + * + * @name jQuery.validator.methods.number + * @type Boolean + */ + jQuery.validator.addMethod("dateformat", function(value, element, params) { + if (this.optional(element)) { + return true; + } + if (params === "DEFAULT") { + return moment(value, moment.javaToMomentDateFormat()).isValid(); + } else { + return moment(value, params, true).isValid(); + } + }, "Please enter a correct date/time"); + + /** + * Replaces the standard number validation to support number with comma. + * + * @name jQuery.validator.methods.number + * @type Boolean + */ + jQuery.validator.methods.number = function(value, element) { + return this.optional(element) || Inputmask.isValid(value, { + alias : "numeric" + }); + }; + + /** + * Replaces the standar min validation to support number with comma. + * + * @name jQuery.validator.methods.number + * @type Boolean + */ + jQuery.validator.methods.min = function(value, element, params) { + var localizedValue = Inputmask.unmask(value, { + alias : "numeric", + unmaskAsNumber : true + }); + return this.optional(element) || localizedValue >= params; + }; + + /** + * Replaces the standar max validation to support number with comma. + * + * @name jQuery.validator.methods.number + * @type Boolean + */ + jQuery.validator.methods.max = function(value, element, params) { + var localizedValue = Inputmask.unmask(value, { + alias : "numeric", + unmaskAsNumber : true + }); + return this.optional(element) || localizedValue <= params; + }; + + /** + * Replaces the standard range validation to support number with comma. + * + * @name jQuery.validator.methods.number + * @type Boolean + */ + jQuery.validator.methods.range = function(value, element, params) { + var localizedValue = Inputmask.unmask(value, { + alias : "numeric", + unmaskAsNumber : true + }); + return this.optional(element) + || (localizedValue >= params[0] && localizedValue <= params[1]); + }; + + /** + * Return true if the field value matches the given RegExp + * + * The difference between `pattern` method is than this one + * use parameter as expression literally (`pattern` includes + * prefix and suffix). + */ + $.validator.addMethod("regexp", function(value, element, param) { + if (this.optional(element)) { + return true; + } + if (typeof param === "string") { + param = new RegExp(param); + } + return param.test(value); + }, "Invalid format."); + + /** + * jquery.inputmask rule: delegates on inputmask control + */ + jQuery.validator.addMethod("inputmask", function(value, element, params) { + if (this.optional(element)) { + return true; + } + var $inputmask = jQuery(element); + return $inputmask.inputmask && $inputmask.inputmask("isComplete"); + }, "Please enter a valid value."); + + // Form validation init + jQuery("form.validate") + .each( + function(index) { + var $form = $(this); + + // see options at https://jqueryvalidation.org/documentation/ + $form + .validate({ + highlight : function(element) { + var $element = $(element); + // añadir marca error + $element.closest('.form-group').addClass('has-error has-feedback'); + // añadir span con icono + var iconSpan = $element.parent().find('span.form-control-feedback'); + if (!iconSpan.length) { + $element + .after(''); + } + }, + unhighlight : function(element) { + var $element = $(element); + // añadir marca error + $element.closest('.form-group').removeClass('has-error has-feedback'); + // quitar span con icono + $element.parent().find('span.form-control-feedback.glyphicon-remove') + .remove(); + // limpiar errores + if ($element.parent('.input-group').length) { + $element.parent('.input-group').parent().find( + 'span.help-block[id=' + $element.attr('id') + '-error]') + .remove(); + + } else { + $element.parent().find( + 'span.help-block[id=' + $element.attr('id') + '-error]') + .remove(); + } + }, + errorElement : 'span', + errorClass : 'help-block', + errorPlacement : function(error, element) { + var $element = $(element); + + var $previousErrors; + if ($element.parent('.input-group').length) { + $previousErrors = $element.parent('.input-group').parent() + .find('span.help-block[id=' + $element.attr('id') + '-error]'); + } else { + $previousErrors = $element.parent().find( + 'span.help-block[id=' + $element.attr('id') + '-error]'); + } + + if ($previousErrors.length === 1) { + $previousErrors.replaceWith(error); + } else { + if ($previousErrors.length > 1) { + // mas de un error limpiamos + $previousErrors.remove(); + } + // insertar error + if ($element.parent('.input-group').length) { + error.insertAfter($element.parent()); + } else { + $element.parent().append(error); + } + } + + } + }); + + // Iterate form inputs to set validation rules and messages + $form.find("input,textarea,select").each(function(index) { + var $input = $(this); + var data = $input.data(); + + // this input validation rules + var rules = { + required : data.required, + messages : {} + }; + if (isNotNull(data.missing)) { + rules.messages.required = data.missing + } + if (isNotNull(data.invalid)) { + rules.messages.remote = data.invalid + } + + // inputmaks + if ($input.hasClass("inputmask")) { + rules["inputmask"] = true; + if (isNotNull(data.invalid)) { + rules.messages.inputmask = data.invalid + } + } + // datetimepicker + if (isNotEmpty(data.dateformat)) { + rules["dateformat"] = moment.javaToMomentDateFormat(data.dateformat); + if (isNotNull(data.invalid)) { + rules.messages.dateformat = data.invalid + } + // datetimepicker without format + } else if ($input.hasClass("datetimepicker")) { + rules["dateformat"] = "DEFAULT"; + if (isNotNull(data.invalid)) { + rules.messages.dateformat = data.invalid + } + } + + $input.rules("add", rules); + }); + }); + }); +})(jQuery); + + diff --git a/spring-roo/src/main/resources/templates/accessibility.html b/spring-roo/src/main/resources/templates/accessibility.html new file mode 100644 index 000000000000..f486646c65bc --- /dev/null +++ b/spring-roo/src/main/resources/templates/accessibility.html @@ -0,0 +1,61 @@ + + + + + Accessibility - Spring Roo application + + + + +
+ +
+ + +
+ + +
+ +
+
+

Accessibility

+

Accessibility policy application

+

+ Spring Roo Application is committed to ensuring the accessibility of + its web content to people with disabilities. All of the content on + our website will meet + W3C WAI's Web Content Accessibility Guidelines 2.0, Level AA + conformance. Any issues should be reported to + springroo@disid.com. + The technologies that is depended to access the accessible content are HTML, CSS and Javascript. +

+
+
+ +
+ +
+ + +
+ +
+ + + + + +
+
+ + + + \ No newline at end of file diff --git a/spring-roo/src/main/resources/templates/books/create.html b/spring-roo/src/main/resources/templates/books/create.html new file mode 100644 index 000000000000..dabe34da1063 --- /dev/null +++ b/spring-roo/src/main/resources/templates/books/create.html @@ -0,0 +1,153 @@ + + + + + + Create Book - roo - SpringRoo Application + + + + + + + + +
+ +
+ + +
+ + +
+
+ +

Create Book

+ + +
+ +
+ Book data + +
+ +
+ + + Error message. +
+
+
+ +
+ + + Error message. +
+
+
+ +
+ + + Error message. +
+
+
+ + + +
+
+ + +
+
+ +
+ + +
+ +
+ +
+ + +
+ +
+ + + + + +
+ + + + + + + + + + + + + + + + + + +
+ + + + \ No newline at end of file diff --git a/spring-roo/src/main/resources/templates/books/edit.html b/spring-roo/src/main/resources/templates/books/edit.html new file mode 100644 index 000000000000..e6d39a44becb --- /dev/null +++ b/spring-roo/src/main/resources/templates/books/edit.html @@ -0,0 +1,171 @@ + + + + + + Edit Book - Spring Roo application + + + + + + + + + +
+ +
+ + +
+ + +
+ +
+ +

Edit Book

+ + +
+ + + + +
+

Warning! This record has been updated by an other user.

+
+ +
+
+ +
+
+ +
+ + +
+ Book data + +
+ +
+ + + Error message. +
+
+
+ +
+ + + Error message. +
+
+
+ +
+ + + Error message. +
+
+
+ + + +
+
+
+ +
+
+ +
+
+
+ +
+ + +
+ +
+ +
+ + +
+ +
+ + + + + +
+ + + + + + + + + + + + + + + + + +
+ + + + diff --git a/spring-roo/src/main/resources/templates/books/list.html b/spring-roo/src/main/resources/templates/books/list.html new file mode 100644 index 000000000000..d7e1fe24ca3b --- /dev/null +++ b/spring-roo/src/main/resources/templates/books/list.html @@ -0,0 +1,98 @@ + + + + + + List Book - roo - SpringRoo Application + + + + + +
+ +
+ + +
+ + +
+
+ +

Books

+ + +
+ + + + + + + + + + + + + + + + + + + + +
Book List
titleauthorisbnTools
titleauthorisbnTools
+ +
+
+
+
+
+
+
+ + + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + + + + +
+
+ + + + diff --git a/spring-roo/src/main/resources/templates/books/show.html b/spring-roo/src/main/resources/templates/books/show.html new file mode 100644 index 000000000000..92311c36f5aa --- /dev/null +++ b/spring-roo/src/main/resources/templates/books/show.html @@ -0,0 +1,86 @@ + + + + + Show Book - Spring Roo application + + + + + +
+ +
+ + +
+ + +
+
+ + +

Show Book

+ +
+

Book data

+
    +
  • + title + titleValue +
  • +
  • + author + authorValue +
  • +
  • + isbn + isbnValue +
  • +
+
+ + +
+
+ Edit +
+
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + + + + +
+
+ + + + diff --git a/spring-roo/src/main/resources/templates/books/showInline.html b/spring-roo/src/main/resources/templates/books/showInline.html new file mode 100644 index 000000000000..9fbc62cf260d --- /dev/null +++ b/spring-roo/src/main/resources/templates/books/showInline.html @@ -0,0 +1,67 @@ + + + + + Show Book - Spring Roo application + + + + + +
+ +
+ + +
+ + +
+
+ + +
+
    +
  • + title + titleValue +
  • +
  • + author + authorValue +
  • +
  • + isbn + isbnValue +
  • +
+
+ +
+ +
+ +
+ + +
+ +
+ + + + + +
+
+ + + + diff --git a/spring-roo/src/main/resources/templates/error.html b/spring-roo/src/main/resources/templates/error.html new file mode 100644 index 000000000000..7559baba22f6 --- /dev/null +++ b/spring-roo/src/main/resources/templates/error.html @@ -0,0 +1,40 @@ + + + + + Error - Spring Roo application + + + + + +
+

Error Page

+ +
+ + +
+ + +
+ +
+ +
+ +
+ +
+ + \ No newline at end of file diff --git a/spring-roo/src/main/resources/templates/fragments/footer.html b/spring-roo/src/main/resources/templates/fragments/footer.html new file mode 100644 index 000000000000..38999c1b7567 --- /dev/null +++ b/spring-roo/src/main/resources/templates/fragments/footer.html @@ -0,0 +1,51 @@ + + + + + Footer + + + + + + + diff --git a/spring-roo/src/main/resources/templates/fragments/header.html b/spring-roo/src/main/resources/templates/fragments/header.html new file mode 100644 index 000000000000..8421d6d77ff4 --- /dev/null +++ b/spring-roo/src/main/resources/templates/fragments/header.html @@ -0,0 +1,21 @@ + + + + + Header + + + +
+
+
+

roo

+

Hello, this is your home page.

+
+
+
+ + \ No newline at end of file diff --git a/spring-roo/src/main/resources/templates/fragments/js/datatables-locale.js b/spring-roo/src/main/resources/templates/fragments/js/datatables-locale.js new file mode 100644 index 000000000000..ad1a32955d18 --- /dev/null +++ b/spring-roo/src/main/resources/templates/fragments/js/datatables-locale.js @@ -0,0 +1,67 @@ +// IIFE - Immediately Invoked Function Expression +(function(translateDatatables) { + + // The global jQuery object is passed as a parameter + translateDatatables(window.jQuery, window, document); + +}(function($, window, document) { + + // The $ is now locally scoped, it won't collide with other libraries + + // Listen for the jQuery ready event on the document + // READY EVENT BEGIN + $(function() { + + // The DOM is ready! + //console.log('The DOM is ready'); + + // Set datatables defaults + $.extend($.fn.dataTable.defaults, { + // multilingual texts definitions, adding some to those already provided + // default by Datatables + 'language': { + 'buttons': { + 'add': /*[[#{label_datatables_add}]]*/ 'Add', + 'delete': /*[[#{label_datatables_delete}]]*/ 'Delete', + 'colvis': /*[[#{label_datatables_columns}]]*/ 'Columns', + 'pageLength': /*[[#{label_datatables_showRows}]]*/ 'Show %d rows' + }, + 'select': { + 'rows': { + _: /*[[#{label_datatables_selectedRows}]]*/ '%d selected rows', + 0: "", + 1: /*[[#{label_datatables_selectedRow}]]*/ '1 selected row' + } + }, + 'decimal': /*[[#{label_datatables_decimal}]]*/ '.', + 'emptyTable': /*[[#{label_datatables_emptyTable}]]*/ 'No data available in table', + 'info': /*[[#{label_datatables_info}]]*/ 'Showing _START_ to _END_ of _TOTAL_ entries', + 'infoEmpty': /*[[#{label_datatables_infoEmpty}]]*/ 'Showing 0 to 0 of 0 entries', + 'infoFiltered': /*[[#{label_datatables_infoFiltered}]]*/ '(filtered from _MAX_ total entries)', + 'infoPostFix': /*[[#{label_datatables_infoPostFix}]]*/ '', + 'thousands': /*[[#{label_datatables_thousands}]]*/ '', + 'lengthMenu': /*[[#{label_datatables_lengthMenu}]]*/ 'Show _MENU_ entries', + 'loadingRecords': /*[[#{label_datatables_loadingRecords}]]*/ 'Loading...', + 'processing': /*[[#{label_datatables_processing}]]*/ 'Processing...', + 'search': /*[[#{label_datatables_search}]]*/ 'Search:', + 'zeroRecords': /*[[#{label_datatables_zeroRecords}]]*/ ' No matching records found', + 'paginate': { + 'first': /*[[#{label_datatables_first}]]*/ 'First', + 'last': /*[[#{label_datatables_last}]]*/ 'Last', + 'next': /*[[#{label_datatables_next}]]*/ 'Next', + 'previous': /*[[#{label_datatables_previous}]]*/ 'Previous' + }, + 'aria': { + 'sortAscending': /*[[#{label_datatables_sortAscending}]]*/ ': activate to sort column ascending', + 'sortDescending': /*[[#{label_datatables_sortDescending}]]*/ ': activate to sort column descending' + } + } + }); + + }); + + // READY EVENT END + //console.log('The DOM may not be ready'); + + // The rest of code goes here! +})); diff --git a/spring-roo/src/main/resources/templates/fragments/js/datatables.html b/spring-roo/src/main/resources/templates/fragments/js/datatables.html new file mode 100644 index 000000000000..5993777eb316 --- /dev/null +++ b/spring-roo/src/main/resources/templates/fragments/js/datatables.html @@ -0,0 +1,78 @@ + + + + + Session info + + + + + +
+ + +
+

Error

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/spring-roo/src/main/resources/templates/fragments/js/select2.html b/spring-roo/src/main/resources/templates/fragments/js/select2.html new file mode 100644 index 000000000000..ce2c6bd7a444 --- /dev/null +++ b/spring-roo/src/main/resources/templates/fragments/js/select2.html @@ -0,0 +1,16 @@ + + + + +Select2 + + +
+ + + +
+ + diff --git a/spring-roo/src/main/resources/templates/fragments/languages.html b/spring-roo/src/main/resources/templates/fragments/languages.html new file mode 100644 index 000000000000..248f9eaea01a --- /dev/null +++ b/spring-roo/src/main/resources/templates/fragments/languages.html @@ -0,0 +1,33 @@ + + + + + Languages + + + + + + + \ No newline at end of file diff --git a/spring-roo/src/main/resources/templates/fragments/menu.html b/spring-roo/src/main/resources/templates/fragments/menu.html new file mode 100644 index 000000000000..86143276e137 --- /dev/null +++ b/spring-roo/src/main/resources/templates/fragments/menu.html @@ -0,0 +1,107 @@ + + + + + Page menu + + + + + + + + + + diff --git a/spring-roo/src/main/resources/templates/fragments/modal-confirm-delete-batch.html b/spring-roo/src/main/resources/templates/fragments/modal-confirm-delete-batch.html new file mode 100644 index 000000000000..3ce82279aced --- /dev/null +++ b/spring-roo/src/main/resources/templates/fragments/modal-confirm-delete-batch.html @@ -0,0 +1,34 @@ + + + + +Modal + + + +
+
+
+

Going to remove the selected elements

+
+
+ + +
+
+ +
+

Removed selected items

+
+ +
+

Error deleting selected items.

+
+ +
+ + \ No newline at end of file diff --git a/spring-roo/src/main/resources/templates/fragments/modal-confirm-delete.html b/spring-roo/src/main/resources/templates/fragments/modal-confirm-delete.html new file mode 100644 index 000000000000..658c04054fa5 --- /dev/null +++ b/spring-roo/src/main/resources/templates/fragments/modal-confirm-delete.html @@ -0,0 +1,35 @@ + + + + +Modal + + + +
+
+
+

Going to remove the selected + element

+
+
+ + +
+
+ +
+

1 Removed item

+
+ +
+

Error deleting selected item.

+
+ +
+ + \ No newline at end of file diff --git a/spring-roo/src/main/resources/templates/fragments/modal-confirm.html b/spring-roo/src/main/resources/templates/fragments/modal-confirm.html new file mode 100644 index 000000000000..7d0fb8d4f8f4 --- /dev/null +++ b/spring-roo/src/main/resources/templates/fragments/modal-confirm.html @@ -0,0 +1,32 @@ + + + + +Modal + + + +
+
+
+ +

Going to remove the selected + element

+ +
+
+ 100% Complete +
+
+
+ +
+
+ + \ No newline at end of file diff --git a/spring-roo/src/main/resources/templates/fragments/modal-export-empty-error.html b/spring-roo/src/main/resources/templates/fragments/modal-export-empty-error.html new file mode 100644 index 000000000000..dd84d8fe9e43 --- /dev/null +++ b/spring-roo/src/main/resources/templates/fragments/modal-export-empty-error.html @@ -0,0 +1,20 @@ + + + + +Modal + + + +
+ +
+

No records found to generate a report.

+
+ +
+ + \ No newline at end of file diff --git a/spring-roo/src/main/resources/templates/fragments/modal.html b/spring-roo/src/main/resources/templates/fragments/modal.html new file mode 100644 index 000000000000..55e13158a49c --- /dev/null +++ b/spring-roo/src/main/resources/templates/fragments/modal.html @@ -0,0 +1,27 @@ + + + + + Modal + + + + + + \ No newline at end of file diff --git a/spring-roo/src/main/resources/templates/fragments/session-links.html b/spring-roo/src/main/resources/templates/fragments/session-links.html new file mode 100644 index 000000000000..786a7a4aec25 --- /dev/null +++ b/spring-roo/src/main/resources/templates/fragments/session-links.html @@ -0,0 +1,46 @@ + + + + + Session info + + + + + + + + + \ No newline at end of file diff --git a/spring-roo/src/main/resources/templates/index.html b/spring-roo/src/main/resources/templates/index.html new file mode 100644 index 000000000000..e4aac5e9a74e --- /dev/null +++ b/spring-roo/src/main/resources/templates/index.html @@ -0,0 +1,150 @@ + + + + + Welcome - SpringRoo Application + + + + + +
+ +
+ + +
+ + +
+
+ +
+ +
+ +
+

With Roo you can easily build full Java applications in minutes.

+

Spring Roo is a next-generation rapid application development tool for Java developers. + It focuses on higher productivity, stock-standard Java APIs, high usability, avoiding engineering trade-offs and + facilitating easy Roo removal. +

+

Thanks for your interest in Spring Roo!

+
+
+
+ + +
+ + +
+
+
+
+ +
+
+

Doc

+ If you are looking for Reference Documentation you can get it here. +
+
+
+
+ + +
+
+
+
+ +
+
+

Project Page

+ All the info about Spring Roo development. +
+
+
+
+ + +
+
+
+
+ +
+
+

Support

+ If you have any question about the project, + you can check it. +
+
+
+
+ + +
+
+
+
+ +
+
+

Code

+ Known, modify and redistribute the source code. +
+
+
+
+ +
+ + +
+
+ +
+
+ +
+
+ +
+ + +
+ +
+ + + + + +
+
+ + + + diff --git a/spring-roo/src/main/resources/templates/layouts/default-layout-no-menu.html b/spring-roo/src/main/resources/templates/layouts/default-layout-no-menu.html new file mode 100644 index 000000000000..40d502800fbf --- /dev/null +++ b/spring-roo/src/main/resources/templates/layouts/default-layout-no-menu.html @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + Spring Roo application + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ +
+ +

Sample static body for direct display of the template

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent + scelerisque neque neque, ac elementum quam dignissim interdum. Phasellus et + placerat elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Praesent scelerisque neque neque, ac elementum quam dignissim interdum. + Phasellus et placerat elit.

+
+ +
+ +
+ + � 2016 Spring Roo (footer for example for direct display of the template) +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + + diff --git a/spring-roo/src/main/resources/templates/layouts/default-layout.html b/spring-roo/src/main/resources/templates/layouts/default-layout.html new file mode 100644 index 000000000000..ba597630d42e --- /dev/null +++ b/spring-roo/src/main/resources/templates/layouts/default-layout.html @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + Spring Roo application + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ +
+ +

Sample static body for direct display of the template

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent + scelerisque neque neque, ac elementum quam dignissim interdum. Phasellus et + placerat elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Praesent scelerisque neque neque, ac elementum quam dignissim interdum. + Phasellus et placerat elit.

+
+ +
+ +
+ + � 2016 Spring Roo (footer for example for direct display of the template) +
+ + + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+ + + + + diff --git a/spring-roo/src/main/resources/templates/layouts/default-list-layout.html b/spring-roo/src/main/resources/templates/layouts/default-list-layout.html new file mode 100644 index 000000000000..23f500224120 --- /dev/null +++ b/spring-roo/src/main/resources/templates/layouts/default-list-layout.html @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + Spring Roo application + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ +
+ +

Sample static body for direct display of the template

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent + scelerisque neque neque, ac elementum quam dignissim interdum. Phasellus et + placerat elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Praesent scelerisque neque neque, ac elementum quam dignissim interdum. + Phasellus et placerat elit.

+
+ +
+ +
+ + � 2016 Spring Roo (footer for example for direct display of the template) +
+ + + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+ + + + + diff --git a/spring-roo/src/main/resources/templates/layouts/home-layout.html b/spring-roo/src/main/resources/templates/layouts/home-layout.html new file mode 100644 index 000000000000..7c1d2f7c5c22 --- /dev/null +++ b/spring-roo/src/main/resources/templates/layouts/home-layout.html @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + Spring Roo application + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +

Sample page header for direct display of the template

+
+
+ +
+ +
+ +

Sample static body for direct display of the template

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent + scelerisque neque neque, ac elementum quam dignissim interdum. Phasellus et + placerat elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Praesent scelerisque neque neque, ac elementum quam dignissim interdum. + Phasellus et placerat elit.

+
+ +
+ +
+ + � 2016 Spring Roo (footer for example for direct display of the template) +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + + diff --git a/spring-roo/src/main/resources/templates/login.html b/spring-roo/src/main/resources/templates/login.html new file mode 100644 index 000000000000..627b165049f5 --- /dev/null +++ b/spring-roo/src/main/resources/templates/login.html @@ -0,0 +1,108 @@ + + + + + Login- Spring Roo application + + + + + +
+ +
+ + +
+ + +
+
+ +
+
+

Login

+
+
+
+
+ Enter your login and password + + + + + + +
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+ + +
+
+
+
+
+
+
+
+ +
+ + +
+ +
+ + + + + +
+
+ + + + diff --git a/spring-roo/src/main/resources/templates/reports/export_default.jrxml b/spring-roo/src/main/resources/templates/reports/export_default.jrxml new file mode 100644 index 000000000000..3540f690de35 --- /dev/null +++ b/spring-roo/src/main/resources/templates/reports/export_default.jrxml @@ -0,0 +1,59 @@ + + + + + + + + + + <band height="94"> + <frame> + <reportElement mode="Opaque" x="-20" y="-20" width="595" height="92" backcolor="#054571" uuid="96670a12-0a0b-4503-8309-d43c1bd41fa5"/> + <staticText> + <reportElement x="20" y="20" width="234" height="43" forecolor="#FFFFFF" uuid="887580cd-50ec-499d-ba19-3971bf3a5552"/> + <textElement> + <font size="34" isBold="true"/> + </textElement> + <text><![CDATA[Export]]></text> + </staticText> + </frame> + <staticText> + <reportElement x="206" y="74" width="100" height="20" uuid="6efdf519-1cda-447d-bb04-5dfca2ae8aed"/> + <text><![CDATA[]]></text> + </staticText> + </band> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-security-basic-auth/README.md b/spring-security-basic-auth/README.md deleted file mode 100644 index 54d09dbf32bf..000000000000 --- a/spring-security-basic-auth/README.md +++ /dev/null @@ -1,15 +0,0 @@ -========= - -## Spring Security with Basic Authentication Example Project - -###The Course -The "Learn Spring Security" Classes: http://github.learnspringsecurity.com - -### Relevant Article: -- [Spring Security - security none, filters none, access permitAll](http://www.baeldung.com/security-none-filters-none-access-permitAll) -- [Spring Security Basic Authentication](http://www.baeldung.com/spring-security-basic-authentication) -- [Writing a Custom Filter in Spring Security](http://www.baeldung.com/spring-security-custom-filter) - - -### Notes -- the project includes both views as well as a REST layer diff --git a/spring-security-basic-auth/pom.xml b/spring-security-basic-auth/pom.xml deleted file mode 100644 index 4cb0efb9e2da..000000000000 --- a/spring-security-basic-auth/pom.xml +++ /dev/null @@ -1,267 +0,0 @@ - - 4.0.0 - com.baeldung - spring-security-mvc-basic-auth - 0.1-SNAPSHOT - - spring-security-mvc-basic-auth - war - - - - - - - org.springframework.security - spring-security-web - ${org.springframework.security.version} - - - org.springframework.security - spring-security-config - ${org.springframework.security.version} - - - - - - org.springframework - spring-core - ${org.springframework.version} - - - commons-logging - commons-logging - - - - - org.springframework - spring-context - ${org.springframework.version} - - - org.springframework - spring-jdbc - ${org.springframework.version} - - - org.springframework - spring-beans - ${org.springframework.version} - - - org.springframework - spring-aop - ${org.springframework.version} - - - org.springframework - spring-tx - ${org.springframework.version} - - - org.springframework - spring-expression - ${org.springframework.version} - - - - org.springframework - spring-web - ${org.springframework.version} - - - org.springframework - spring-webmvc - ${org.springframework.version} - - - - - - javax.servlet - javax.servlet-api - ${javax.servlet.version} - provided - - - - javax.servlet - jstl - ${jstl.version} - runtime - - - - - - com.google.guava - guava - ${guava.version} - - - - - - org.slf4j - slf4j-api - ${org.slf4j.version} - - - ch.qos.logback - logback-classic - ${logback.version} - - - - org.slf4j - jcl-over-slf4j - ${org.slf4j.version} - - - - org.slf4j - log4j-over-slf4j - ${org.slf4j.version} - - - - - - junit - junit - ${junit.version} - test - - - - org.hamcrest - hamcrest-core - ${org.hamcrest.version} - test - - - org.hamcrest - hamcrest-library - ${org.hamcrest.version} - test - - - - org.mockito - mockito-core - ${mockito.version} - test - - - - - - spring-security-mvc-basic-auth - - - src/main/resources - true - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - ${maven-compiler-plugin.version} - - 1.8 - 1.8 - - - - - org.apache.maven.plugins - maven-war-plugin - ${maven-war-plugin.version} - - - - org.apache.maven.plugins - maven-surefire-plugin - ${maven-surefire-plugin.version} - - - - - - - - - - - - org.codehaus.cargo - cargo-maven2-plugin - ${cargo-maven2-plugin.version} - - true - - jetty8x - embedded - - - - - - - 8082 - - - - - - - - - - - - 4.3.4.RELEASE - 4.2.0.RELEASE - - - 5.2.5.Final - 5.1.40 - - - 1.7.21 - 1.1.7 - - - 5.3.3.Final - 1.2 - 3.1.0 - - - 19.0 - 3.5 - - - 1.3 - 4.12 - 1.10.19 - - 4.4.5 - 4.5.2 - - 2.9.0 - - - 3.6.0 - 2.6 - 2.19.1 - 2.7 - 1.6.1 - - - - \ No newline at end of file diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/persistence/service/FooService.java b/spring-security-basic-auth/src/main/java/org/baeldung/persistence/service/FooService.java deleted file mode 100644 index 02db7a733a6b..000000000000 --- a/spring-security-basic-auth/src/main/java/org/baeldung/persistence/service/FooService.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.baeldung.persistence.service; - -import org.baeldung.web.dto.Foo; -import org.springframework.stereotype.Service; - -@Service -public class FooService { - - public FooService() { - super(); - } - - // API - - public Foo getById(final Long id) { - return null; - } - - public Long create(final Foo resource) { - return null; - } - -} diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/spring/MvcConfig.java b/spring-security-basic-auth/src/main/java/org/baeldung/spring/MvcConfig.java deleted file mode 100644 index 74c11478ee2e..000000000000 --- a/spring-security-basic-auth/src/main/java/org/baeldung/spring/MvcConfig.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.baeldung.spring; - -import org.springframework.context.annotation.Bean; -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.ViewControllerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; -import org.springframework.web.servlet.view.InternalResourceViewResolver; -import org.springframework.web.servlet.view.JstlView; - -@Configuration -@EnableWebMvc -public class MvcConfig extends WebMvcConfigurerAdapter { - - public MvcConfig() { - super(); - } - - // API - - @Override - public void addViewControllers(final ViewControllerRegistry registry) { - super.addViewControllers(registry); - - registry.addViewController("/homepage.html"); - } - - @Bean - public ViewResolver viewResolver() { - final InternalResourceViewResolver bean = new InternalResourceViewResolver(); - - bean.setViewClass(JstlView.class); - bean.setPrefix("/WEB-INF/view/"); - bean.setSuffix(".jsp"); - - return bean; - } -} \ No newline at end of file diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/spring/PersistenceConfig.java b/spring-security-basic-auth/src/main/java/org/baeldung/spring/PersistenceConfig.java deleted file mode 100644 index 4ea0053f4845..000000000000 --- a/spring-security-basic-auth/src/main/java/org/baeldung/spring/PersistenceConfig.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.baeldung.spring; - -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; - -@Configuration -@ComponentScan("org.baeldung.persistence") -public class PersistenceConfig { - - public PersistenceConfig() { - super(); - } - -} diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/spring/SecSecurityConfig.java b/spring-security-basic-auth/src/main/java/org/baeldung/spring/SecSecurityConfig.java deleted file mode 100644 index f40c5998343f..000000000000 --- a/spring-security-basic-auth/src/main/java/org/baeldung/spring/SecSecurityConfig.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.baeldung.spring; - -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; - -@Configuration -@ComponentScan("org.baeldung.security") -// @ImportResource({ "classpath:webSecurityConfig.xml" }) -public class SecSecurityConfig { - - public SecSecurityConfig() { - super(); - } - -} diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/spring/WebConfig.java b/spring-security-basic-auth/src/main/java/org/baeldung/spring/WebConfig.java deleted file mode 100644 index fa6f5f6d5626..000000000000 --- a/spring-security-basic-auth/src/main/java/org/baeldung/spring/WebConfig.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.baeldung.spring; - -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; - -@Configuration -@ComponentScan("org.baeldung.web") -public class WebConfig extends WebMvcConfigurerAdapter { - - public WebConfig() { - super(); - } - - // API - -} \ No newline at end of file diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/FooController.java b/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/FooController.java deleted file mode 100644 index daa797ee368e..000000000000 --- a/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/FooController.java +++ /dev/null @@ -1,74 +0,0 @@ -package org.baeldung.web.controller; - -import java.net.URI; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.baeldung.persistence.service.FooService; -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; -import org.springframework.web.util.UriTemplate; - -import com.google.common.base.Preconditions; - -@Controller -@RequestMapping(value = "/foo") -public class FooController { - - @Autowired - private ApplicationEventPublisher eventPublisher; - - @Autowired - private FooService service; - - public FooController() { - super(); - } - - // API - - @RequestMapping(value = "/{id}", method = RequestMethod.GET) - @ResponseBody - public Foo findOne(@PathVariable("id") final Long id, final UriComponentsBuilder uriBuilder, final HttpServletResponse response) { - return new Foo(); - } - - @RequestMapping(value = "admin/foo/{id}", method = RequestMethod.GET) - @ResponseBody - public Foo get(@PathVariable("id") final Long id, final HttpServletRequest request, final HttpServletResponse response) { - final Foo resourceById = Preconditions.checkNotNull(service.getById(id)); - - eventPublisher.publishEvent(new SingleResourceRetrieved(this, request, response)); - return resourceById; - } - - @RequestMapping(value = "admin/foo", method = RequestMethod.POST) - @ResponseStatus(HttpStatus.CREATED) - public void create(@RequestBody final Foo resource, final HttpServletRequest request, final HttpServletResponse response) { - Preconditions.checkNotNull(resource); - final Long idOfCreatedResource = service.create(resource); - - eventPublisher.publishEvent(new ResourceCreated(this, request, response, idOfCreatedResource)); - } - - @RequestMapping(value = "admin", method = RequestMethod.GET) - @ResponseStatus(value = HttpStatus.NO_CONTENT) - public void adminRoot(final HttpServletRequest request, final HttpServletResponse response) { - final String rootUri = request.getRequestURL().toString(); - - final URI fooUri = new UriTemplate("{rootUri}/{resource}").expand(rootUri, "foo"); - final String linkToFoo = LinkUtil.createLinkHeader(fooUri.toASCIIString(), "collection"); - response.addHeader("Link", linkToFoo); - } -} diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/LinkUtil.java b/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/LinkUtil.java deleted file mode 100644 index a41ebb5a5cdd..000000000000 --- a/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/LinkUtil.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.baeldung.web.controller; - -import javax.servlet.http.HttpServletResponse; - -/** - * Provides some constants and utility methods to build a Link Header to be stored in the {@link HttpServletResponse} object - */ -public final class LinkUtil { - - private LinkUtil() { - throw new AssertionError(); - } - - // - - /** - * Creates a Link Header to be stored in the {@link HttpServletResponse} to provide Discoverability features to the user - * - * @param uri - * the base uri - * @param rel - * the relative path - * - * @return the complete url - */ - public static String createLinkHeader(final String uri, final String rel) { - return "<" + uri + ">; rel=\"" + rel + "\""; - } - -} diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/ResourceCreated.java b/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/ResourceCreated.java deleted file mode 100644 index a67788810161..000000000000 --- a/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/ResourceCreated.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.baeldung.web.controller; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.context.ApplicationEvent; - -public class ResourceCreated extends ApplicationEvent { - private final HttpServletResponse response; - private final HttpServletRequest request; - private final long idOfNewResource; - - public ResourceCreated(final Object source, final HttpServletRequest request, final HttpServletResponse response, final long idOfNewResource) { - super(source); - - this.request = request; - this.response = response; - this.idOfNewResource = idOfNewResource; - } - - // API - - public HttpServletResponse getResponse() { - return response; - } - - public HttpServletRequest getRequest() { - return request; - } - - public long getIdOfNewResource() { - return idOfNewResource; - } - -} diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/ResourceCreatedDiscoverabilityListener.java b/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/ResourceCreatedDiscoverabilityListener.java deleted file mode 100644 index 8d19ef82fc63..000000000000 --- a/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/ResourceCreatedDiscoverabilityListener.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.baeldung.web.controller; - -import java.net.URI; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.context.ApplicationListener; -import org.springframework.stereotype.Component; -import org.springframework.web.util.UriTemplate; - -import com.google.common.base.Preconditions; -import com.google.common.net.HttpHeaders; - -@Component -class ResourceCreatedDiscoverabilityListener implements ApplicationListener { - - @Override - public void onApplicationEvent(final ResourceCreated resourceCreatedEvent) { - Preconditions.checkNotNull(resourceCreatedEvent); - - final HttpServletRequest request = resourceCreatedEvent.getRequest(); - final HttpServletResponse response = resourceCreatedEvent.getResponse(); - final long idOfNewResource = resourceCreatedEvent.getIdOfNewResource(); - - addLinkHeaderOnResourceCreation(request, response, idOfNewResource); - } - - void addLinkHeaderOnResourceCreation(final HttpServletRequest request, final HttpServletResponse response, final long idOfNewResource) { - final String requestUrl = request.getRequestURL().toString(); - final URI uri = new UriTemplate("{requestUrl}/{idOfNewResource}").expand(requestUrl, idOfNewResource); - response.setHeader(HttpHeaders.LOCATION, uri.toASCIIString()); - } - -} \ No newline at end of file diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/SingleResourceRetrieved.java b/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/SingleResourceRetrieved.java deleted file mode 100644 index 3de791810549..000000000000 --- a/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/SingleResourceRetrieved.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.baeldung.web.controller; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.context.ApplicationEvent; - -public class SingleResourceRetrieved extends ApplicationEvent { - private final HttpServletResponse response; - private final HttpServletRequest request; - - public SingleResourceRetrieved(final Object source, final HttpServletRequest request, final HttpServletResponse response) { - super(source); - - this.request = request; - this.response = response; - } - - // API - - public HttpServletResponse getResponse() { - return response; - } - - public HttpServletRequest getRequest() { - return request; - } - -} diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/SingleResourceRetrievedDiscoverabilityListener.java b/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/SingleResourceRetrievedDiscoverabilityListener.java deleted file mode 100644 index 45cd7c4d131a..000000000000 --- a/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/SingleResourceRetrievedDiscoverabilityListener.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.baeldung.web.controller; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.context.ApplicationListener; -import org.springframework.stereotype.Component; - -import com.google.common.base.Preconditions; - -@Component -class SingleResourceRetrievedDiscoverabilityListener implements ApplicationListener { - - @Override - public void onApplicationEvent(final SingleResourceRetrieved resourceRetrievedEvent) { - Preconditions.checkNotNull(resourceRetrievedEvent); - - final HttpServletRequest request = resourceRetrievedEvent.getRequest(); - final HttpServletResponse response = resourceRetrievedEvent.getResponse(); - addLinkHeaderOnSingleResourceRetrieval(request, response); - } - - void addLinkHeaderOnSingleResourceRetrieval(final HttpServletRequest request, final HttpServletResponse response) { - final StringBuffer requestURL = request.getRequestURL(); - final int positionOfLastSlash = requestURL.lastIndexOf("/"); - final String uriForResourceCreation = requestURL.substring(0, positionOfLastSlash); - - final String linkHeaderValue = LinkUtil.createLinkHeader(uriForResourceCreation, "collection"); - response.addHeader("Link", linkHeaderValue); - } - -} \ No newline at end of file diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/TestController.java b/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/TestController.java deleted file mode 100644 index f68cfb2eb7dc..000000000000 --- a/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/TestController.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.baeldung.web.controller; - -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseBody; - -@Controller -public class TestController { - - public TestController() { - super(); - } - - // API - - @RequestMapping("/permitAll") - @ResponseBody - public String permitAll() { - return "Permit All"; - } - - @RequestMapping("/securityNone") - @ResponseBody - public String securityNone() { - return "Security None"; - } - -} diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/web/dto/Foo.java b/spring-security-basic-auth/src/main/java/org/baeldung/web/dto/Foo.java deleted file mode 100644 index 352045989daa..000000000000 --- a/spring-security-basic-auth/src/main/java/org/baeldung/web/dto/Foo.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.baeldung.web.dto; - -import java.io.Serializable; - -public class Foo implements Serializable { - - public Foo() { - super(); - } - -} diff --git a/spring-security-basic-auth/src/main/resources/logback.xml b/spring-security-basic-auth/src/main/resources/logback.xml deleted file mode 100644 index 1146dade632e..000000000000 --- a/spring-security-basic-auth/src/main/resources/logback.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/spring-security-basic-auth/src/main/resources/webSecurityConfig.xml b/spring-security-basic-auth/src/main/resources/webSecurityConfig.xml deleted file mode 100644 index f6d15980aef6..000000000000 --- a/spring-security-basic-auth/src/main/resources/webSecurityConfig.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/spring-security-basic-auth/src/main/webapp/WEB-INF/mvc-servlet.xml b/spring-security-basic-auth/src/main/webapp/WEB-INF/mvc-servlet.xml deleted file mode 100644 index eb7ce7b5f8a3..000000000000 --- a/spring-security-basic-auth/src/main/webapp/WEB-INF/mvc-servlet.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/spring-security-basic-auth/src/main/webapp/WEB-INF/view/homepage.jsp b/spring-security-basic-auth/src/main/webapp/WEB-INF/view/homepage.jsp deleted file mode 100644 index 7cc14b5dcddf..000000000000 --- a/spring-security-basic-auth/src/main/webapp/WEB-INF/view/homepage.jsp +++ /dev/null @@ -1,7 +0,0 @@ - - - - -

This is the body of the sample view

- - \ No newline at end of file diff --git a/spring-security-basic-auth/src/main/webapp/WEB-INF/web.xml b/spring-security-basic-auth/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index 77a830c6d5e5..000000000000 --- a/spring-security-basic-auth/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - Spring Security Basic Auth Application - - - contextClass - org.springframework.web.context.support.AnnotationConfigWebApplicationContext - - - contextConfigLocation - org.baeldung.spring - - - - org.springframework.web.context.ContextLoaderListener - - - - mvc - org.springframework.web.servlet.DispatcherServlet - 1 - - - mvc - / - - - - springSecurityFilterChain - org.springframework.web.filter.DelegatingFilterProxy - - - springSecurityFilterChain - /* - - - - index.html - - - \ No newline at end of file diff --git a/spring-security-basic-auth/src/test/resources/.gitignore b/spring-security-basic-auth/src/test/resources/.gitignore deleted file mode 100644 index 83c05e60c802..000000000000 --- a/spring-security-basic-auth/src/test/resources/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -*.class - -#folders# -/target -/neoDb* -/data -/src/main/webapp/WEB-INF/classes -*/META-INF/* - -# Packaged files # -*.jar -*.war -*.ear \ No newline at end of file diff --git a/spring-security-cache-control/README.md b/spring-security-cache-control/README.md new file mode 100644 index 000000000000..4b0bab72cb83 --- /dev/null +++ b/spring-security-cache-control/README.md @@ -0,0 +1,6 @@ +========= + +## Spring Security Cache Control + +### Relevant Articles: +- [Spring Security – Cache Control Headers](http://www.baeldung.com/spring-security-cache-control-headers) diff --git a/spring-security-cache-control/pom.xml b/spring-security-cache-control/pom.xml index c30b0cd1aa32..b24b9db484fb 100644 --- a/spring-security-cache-control/pom.xml +++ b/spring-security-cache-control/pom.xml @@ -81,8 +81,27 @@ + UTF-8 + 1.8 + 3.1.0 2.9.0 + + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*IntegrationTest.java + **/*LiveTest.java + + + + + + + \ No newline at end of file diff --git a/spring-security-cache-control/src/test/java/com/baeldung/cachecontrol/ResourceEndpointTest.java b/spring-security-cache-control/src/test/java/com/baeldung/cachecontrol/ResourceEndpointIntegrationTest.java similarity index 50% rename from spring-security-cache-control/src/test/java/com/baeldung/cachecontrol/ResourceEndpointTest.java rename to spring-security-cache-control/src/test/java/com/baeldung/cachecontrol/ResourceEndpointIntegrationTest.java index 6d532f98fc13..2c4887891702 100644 --- a/spring-security-cache-control/src/test/java/com/baeldung/cachecontrol/ResourceEndpointTest.java +++ b/spring-security-cache-control/src/test/java/com/baeldung/cachecontrol/ResourceEndpointIntegrationTest.java @@ -1,68 +1,45 @@ package com.baeldung.cachecontrol; -import com.jayway.restassured.http.ContentType; +import static com.jayway.restassured.RestAssured.given; + import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.context.embedded.LocalServerPort; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; -import static com.jayway.restassured.RestAssured.given; +import com.jayway.restassured.http.ContentType; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = AppRunner.class) -public class ResourceEndpointTest { +public class ResourceEndpointIntegrationTest { @LocalServerPort private int serverPort; @Test public void whenGetRequestForUser_shouldRespondWithDefaultCacheHeaders() { - given() - .when() - .get(getBaseUrl() + "/default/users/Michael") - .then() - .headers("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate") - .header("Pragma", "no-cache"); + given().when().get(getBaseUrl() + "/default/users/Michael").then().headers("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate").header("Pragma", "no-cache"); } @Test public void whenGetRequestForUser_shouldRespondMaxAgeCacheControl() { - given() - .when() - .get(getBaseUrl() + "/users/Michael") - .then() - .header("Cache-Control", "max-age=60"); + given().when().get(getBaseUrl() + "/users/Michael").then().header("Cache-Control", "max-age=60"); } @Test public void givenServiceEndpoint_whenGetRequestForUser_shouldResponseWithCacheControlMaxAge() { - given() - .when() - .get(getBaseUrl() + "/users/Michael") - .then() - .contentType(ContentType.JSON).and().statusCode(200).and() - .header("Cache-Control", "max-age=60"); + given().when().get(getBaseUrl() + "/users/Michael").then().contentType(ContentType.JSON).and().statusCode(200).and().header("Cache-Control", "max-age=60"); } @Test public void givenServiceEndpoint_whenGetRequestForNotCacheableContent_shouldResponseWithCacheControlNoCache() { - given() - .when() - .get(getBaseUrl() + "/timestamp") - .then() - .contentType(ContentType.JSON).and().statusCode(200).and() - .header("Cache-Control", "no-store"); + given().when().get(getBaseUrl() + "/timestamp").then().contentType(ContentType.JSON).and().statusCode(200).and().header("Cache-Control", "no-store"); } @Test public void givenServiceEndpoint_whenGetRequestForPrivateUser_shouldResponseWithSecurityDefaultCacheControl() { - given() - .when() - .get(getBaseUrl() + "/private/users/Michael") - .then() - .contentType(ContentType.JSON).and().statusCode(200).and() - .header("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate"); + given().when().get(getBaseUrl() + "/private/users/Michael").then().contentType(ContentType.JSON).and().statusCode(200).and().header("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate"); } private String getBaseUrl() { diff --git a/spring-security-core/README.md b/spring-security-core/README.md index 0eb506e865a3..3675e7e160f8 100644 --- a/spring-security-core/README.md +++ b/spring-security-core/README.md @@ -8,3 +8,4 @@ mvn clean install ### Relevant Articles: - [Intro to @PreFilter and @PostFilter in Spring Security](http://www.baeldung.com/spring-security-prefilter-postfilter) +- [Spring Boot Authentication Auditing Support](http://www.baeldung.com/spring-boot-authentication-audit) diff --git a/spring-security-core/pom.xml b/spring-security-core/pom.xml index a8ffce84b776..971f5a9d0fcc 100644 --- a/spring-security-core/pom.xml +++ b/spring-security-core/pom.xml @@ -32,6 +32,10 @@ org.springframework.boot spring-boot-starter-thymeleaf + + org.springframework.boot + spring-boot-starter-actuator + com.h2database h2 diff --git a/spring-security-core/src/main/java/org/baeldung/auditing/ExposeAttemptedPathAuthorizationAuditListener.java b/spring-security-core/src/main/java/org/baeldung/auditing/ExposeAttemptedPathAuthorizationAuditListener.java new file mode 100644 index 000000000000..bc36ac08b3a7 --- /dev/null +++ b/spring-security-core/src/main/java/org/baeldung/auditing/ExposeAttemptedPathAuthorizationAuditListener.java @@ -0,0 +1,36 @@ +package org.baeldung.auditing; + +import org.springframework.boot.actuate.audit.AuditEvent; +import org.springframework.boot.actuate.security.AbstractAuthorizationAuditListener; +import org.springframework.security.access.event.AbstractAuthorizationEvent; +import org.springframework.security.access.event.AuthorizationFailureEvent; +import org.springframework.security.web.FilterInvocation; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +@Component +public class ExposeAttemptedPathAuthorizationAuditListener extends AbstractAuthorizationAuditListener { + + public static final String AUTHORIZATION_FAILURE = "AUTHORIZATION_FAILURE"; + + @Override + public void onApplicationEvent(AbstractAuthorizationEvent event) { + if (event instanceof AuthorizationFailureEvent) { + onAuthorizationFailureEvent((AuthorizationFailureEvent) event); + } + } + + private void onAuthorizationFailureEvent(AuthorizationFailureEvent event) { + Map data = new HashMap<>(); + data.put("type", event.getAccessDeniedException().getClass().getName()); + data.put("message", event.getAccessDeniedException().getMessage()); + data.put("requestUrl", ((FilterInvocation)event.getSource()).getRequestUrl() ); + if (event.getAuthentication().getDetails() != null) { + data.put("details", event.getAuthentication().getDetails()); + } + publish(new AuditEvent(event.getAuthentication().getName(), AUTHORIZATION_FAILURE, + data)); + } +} diff --git a/spring-security-core/src/main/java/org/baeldung/auditing/LoginAttemptsLogger.java b/spring-security-core/src/main/java/org/baeldung/auditing/LoginAttemptsLogger.java new file mode 100644 index 000000000000..bf0781bced20 --- /dev/null +++ b/spring-security-core/src/main/java/org/baeldung/auditing/LoginAttemptsLogger.java @@ -0,0 +1,25 @@ +package org.baeldung.auditing; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.actuate.audit.AuditEvent; +import org.springframework.boot.actuate.audit.listener.AuditApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.security.web.authentication.WebAuthenticationDetails; +import org.springframework.stereotype.Component; + +@Component +public class LoginAttemptsLogger { + private static final Logger LOGGER = LoggerFactory.getLogger(LoginAttemptsLogger.class); + + @EventListener + public void auditEventHappened(AuditApplicationEvent auditApplicationEvent) { + AuditEvent auditEvent = auditApplicationEvent.getAuditEvent(); + LOGGER.info("Principal " + auditEvent.getPrincipal() + " - " + auditEvent.getType()); + + WebAuthenticationDetails details = (WebAuthenticationDetails) auditEvent.getData().get("details"); + LOGGER.info(" Remote IP address: " + details.getRemoteAddress()); + LOGGER.info(" Session Id: " + details.getSessionId()); + LOGGER.info(" Request URL: " + auditEvent.getData().get("requestUrl")); + } +} diff --git a/spring-security-core/src/main/java/org/baeldung/config/WebSecurityConfig.java b/spring-security-core/src/main/java/org/baeldung/config/WebSecurityConfig.java index 5441dac7f77f..0b6cd34f3e2d 100644 --- a/spring-security-core/src/main/java/org/baeldung/config/WebSecurityConfig.java +++ b/spring-security-core/src/main/java/org/baeldung/config/WebSecurityConfig.java @@ -20,6 +20,9 @@ protected void configure(HttpSecurity http) throws Exception { @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - auth.inMemoryAuthentication().withUser("jim").password("jim").roles("USER").and().withUser("pam").password("pam").roles("USER").and().withUser("michael").password("michael").roles("MANAGER"); + auth.inMemoryAuthentication() + .withUser("jim").password("jim").roles("USER", "ACTUATOR") + .and().withUser("pam").password("pam").roles("USER") + .and().withUser("michael").password("michael").roles("MANAGER"); } } diff --git a/spring-security-mvc-boot/README.MD b/spring-security-mvc-boot/README.MD index 3e789dedadee..70b0f23cbbc1 100644 --- a/spring-security-mvc-boot/README.MD +++ b/spring-security-mvc-boot/README.MD @@ -6,3 +6,4 @@ The "REST With Spring" Classes: http://github.learnspringsecurity.com - [Custom AccessDecisionVoters in Spring Security](http://www.baeldung.com/spring-security-custom-voter) - [Spring Security: Authentication with a Database-backed UserDetailsService](http://www.baeldung.com/spring-security-authentication-with-a-database) - [Two Login Pages with Spring Security](http://www.baeldung.com/spring-security-two-login-pages) +- [Multiple Entry Points in Spring Security](http://www.baeldung.com/spring-security-multiple-entry-points) diff --git a/spring-security-mvc-boot/pom.xml b/spring-security-mvc-boot/pom.xml index 591ededccf56..0b113f4cbf82 100644 --- a/spring-security-mvc-boot/pom.xml +++ b/spring-security-mvc-boot/pom.xml @@ -14,19 +14,19 @@ org.springframework.boot spring-boot-starter-parent - 1.4.4.RELEASE + 1.5.2.RELEASE + org.springframework.boot spring-boot-starter-security - + org.springframework.boot spring-boot-starter-web - org.springframework.boot spring-boot-starter-tomcat @@ -54,13 +54,13 @@ com.h2database h2 - + org.springframework.boot spring-boot-starter-test test - + junit junit @@ -97,6 +97,12 @@ spring-test test + + + org.springframework.security + spring-security-test + test + org.apache.derby @@ -140,7 +146,7 @@ jstl-api ${jstl.version} - + @@ -158,6 +164,7 @@ **/*IntegrationTest.java **/*LiveTest.java + **/*EntryPointsTest.java @@ -279,6 +286,43 @@ + + + entryPoints + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + integration-test + + test + + + + **/*LiveTest.java + **/*IntegrationTest.java + + + **/*EntryPointsTest.java + + + + + + + json + + + + + + + @@ -288,12 +332,16 @@ + + + UTF-8 1.8 10.13.1.1 1.1.2 - 4.2.0.RELEASE - 4.2.0.RELEASE + 4.2.2.RELEASE + 4.2.2.RELEASE + 4.2.2.RELEASE 1.2 2.4.0 diff --git a/spring-security-mvc-boot/src/main/java/org/baeldung/Application.java b/spring-security-mvc-boot/src/main/java/org/baeldung/Application.java index 03de5897f51b..fa10799caab4 100644 --- a/spring-security-mvc-boot/src/main/java/org/baeldung/Application.java +++ b/spring-security-mvc-boot/src/main/java/org/baeldung/Application.java @@ -2,15 +2,15 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.context.web.SpringBootServletInitializer; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; @Configuration @EnableAutoConfiguration -@ComponentScan(excludeFilters = { @ComponentScan.Filter(type = FilterType.REGEX, pattern = "org.baeldung.voter.*"), @ComponentScan.Filter(type = FilterType.REGEX, pattern = "org.baeldung.multiplelogin.*") }) -public class Application extends SpringBootServletInitializer { +@ComponentScan(excludeFilters = { @ComponentScan.Filter(type = FilterType.REGEX, pattern = "org.baeldung.voter.*"), @ComponentScan.Filter(type = FilterType.REGEX, pattern = "org.baeldung.multiplelogin.*"), + @ComponentScan.Filter(type = FilterType.REGEX, pattern = "org.baeldung.multipleentrypoints.*") }) +public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } diff --git a/spring-security-mvc-boot/src/main/java/org/baeldung/config/SecurityConfig.java b/spring-security-mvc-boot/src/main/java/org/baeldung/config/SecurityConfig.java index 8cc9d45823bb..acb7e6820a9e 100644 --- a/spring-security-mvc-boot/src/main/java/org/baeldung/config/SecurityConfig.java +++ b/spring-security-mvc-boot/src/main/java/org/baeldung/config/SecurityConfig.java @@ -24,7 +24,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(final AuthenticationManagerBuilder auth) throws Exception { - auth.userDetailsService(userDetailsService); + auth.authenticationProvider(authenticationProvider()); } @Override diff --git a/spring-security-mvc-boot/src/main/java/org/baeldung/multipleentrypoints/MultipleEntryPointsApplication.java b/spring-security-mvc-boot/src/main/java/org/baeldung/multipleentrypoints/MultipleEntryPointsApplication.java new file mode 100644 index 000000000000..4e5fafcd99ee --- /dev/null +++ b/spring-security-mvc-boot/src/main/java/org/baeldung/multipleentrypoints/MultipleEntryPointsApplication.java @@ -0,0 +1,12 @@ +package org.baeldung.multipleentrypoints; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +// @ImportResource({"classpath*:spring-security-multiple-entry.xml"}) +public class MultipleEntryPointsApplication { + public static void main(String[] args) { + SpringApplication.run(MultipleEntryPointsApplication.class, args); + } +} diff --git a/spring-security-mvc-boot/src/main/java/org/baeldung/multipleentrypoints/MultipleEntryPointsSecurityConfig.java b/spring-security-mvc-boot/src/main/java/org/baeldung/multipleentrypoints/MultipleEntryPointsSecurityConfig.java new file mode 100644 index 000000000000..9f2eba0a2e8a --- /dev/null +++ b/spring-security-mvc-boot/src/main/java/org/baeldung/multipleentrypoints/MultipleEntryPointsSecurityConfig.java @@ -0,0 +1,92 @@ +package org.baeldung.multipleentrypoints; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; +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; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; +import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; + +@Configuration +@EnableWebSecurity +public class MultipleEntryPointsSecurityConfig { + + @Bean + public UserDetailsService userDetailsService() throws Exception { + InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); + manager.createUser(User.withUsername("user").password("userPass").roles("USER").build()); + manager.createUser(User.withUsername("admin").password("adminPass").roles("ADMIN").build()); + return manager; + } + + @Configuration + @Order(1) + public static class App1ConfigurationAdapter extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + //@formatter:off + http.antMatcher("/admin/**") + .authorizeRequests().anyRequest().hasRole("ADMIN") + .and().httpBasic().authenticationEntryPoint(authenticationEntryPoint()) + .and().exceptionHandling().accessDeniedPage("/403"); + //@formatter:on + } + + @Bean + public AuthenticationEntryPoint authenticationEntryPoint(){ + BasicAuthenticationEntryPoint entryPoint = new BasicAuthenticationEntryPoint(); + entryPoint.setRealmName("admin realm"); + return entryPoint; + } + } + + @Configuration + @Order(2) + public static class App2ConfigurationAdapter extends WebSecurityConfigurerAdapter { + + protected void configure(HttpSecurity http) throws Exception { + + //@formatter:off + http.antMatcher("/user/**") + .authorizeRequests().anyRequest().hasRole("USER") + .and().formLogin().loginProcessingUrl("/user/login") + .failureUrl("/userLogin?error=loginError").defaultSuccessUrl("/user/myUserPage") + .and().logout().logoutUrl("/user/logout").logoutSuccessUrl("/multipleHttpLinks") + .deleteCookies("JSESSIONID") + .and().exceptionHandling() + .defaultAuthenticationEntryPointFor(loginUrlauthenticationEntryPointWithWarning(), new AntPathRequestMatcher("/user/private/**")) + .defaultAuthenticationEntryPointFor(loginUrlauthenticationEntryPoint(), new AntPathRequestMatcher("/user/general/**")) + .accessDeniedPage("/403") + .and().csrf().disable(); + //@formatter:on + } + + @Bean + public AuthenticationEntryPoint loginUrlauthenticationEntryPoint(){ + return new LoginUrlAuthenticationEntryPoint("/userLogin"); + } + + @Bean + public AuthenticationEntryPoint loginUrlauthenticationEntryPointWithWarning(){ + return new LoginUrlAuthenticationEntryPoint("/userLoginWithWarning"); + } + } + + @Configuration + @Order(3) + public static class App3ConfigurationAdapter extends WebSecurityConfigurerAdapter { + + protected void configure(HttpSecurity http) throws Exception { + http.antMatcher("/guest/**").authorizeRequests().anyRequest().permitAll(); + } + } + +} diff --git a/spring-security-mvc-boot/src/main/java/org/baeldung/multipleentrypoints/PagesController.java b/spring-security-mvc-boot/src/main/java/org/baeldung/multipleentrypoints/PagesController.java new file mode 100644 index 000000000000..b3462d40616b --- /dev/null +++ b/spring-security-mvc-boot/src/main/java/org/baeldung/multipleentrypoints/PagesController.java @@ -0,0 +1,48 @@ +package org.baeldung.multipleentrypoints; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; + +@Controller +public class PagesController { + + @RequestMapping("/multipleHttpLinks") + public String getMultipleHttpLinksPage() { + return "multipleHttpElems/multipleHttpLinks"; + } + + @RequestMapping("/admin/myAdminPage") + public String getAdminPage() { + return "multipleHttpElems/myAdminPage"; + } + + @RequestMapping("/user/general/myUserPage") + public String getUserPage() { + return "multipleHttpElems/myUserPage"; + } + + @RequestMapping("/user/private/myPrivateUserPage") + public String getPrivateUserPage() { + return "multipleHttpElems/myPrivateUserPage"; + } + + @RequestMapping("/guest/myGuestPage") + public String getGuestPage() { + return "multipleHttpElems/myGuestPage"; + } + + @RequestMapping("/userLogin") + public String getUserLoginPage() { + return "multipleHttpElems/login"; + } + + @RequestMapping("/userLoginWithWarning") + public String getUserLoginPageWithWarning() { + return "multipleHttpElems/loginWithWarning"; + } + + @RequestMapping("/403") + public String getAccessDeniedPage() { + return "403"; + } +} diff --git a/spring-security-mvc-boot/src/main/java/org/baeldung/multiplelogin/MultipleLoginApplication.java b/spring-security-mvc-boot/src/main/java/org/baeldung/multiplelogin/MultipleLoginApplication.java index 836336eb71f7..e9dc541ad387 100644 --- a/spring-security-mvc-boot/src/main/java/org/baeldung/multiplelogin/MultipleLoginApplication.java +++ b/spring-security-mvc-boot/src/main/java/org/baeldung/multiplelogin/MultipleLoginApplication.java @@ -2,7 +2,6 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.context.web.SpringBootServletInitializer; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -10,13 +9,8 @@ @SpringBootApplication @ComponentScan("org.baeldung.multiplelogin") -public class MultipleLoginApplication extends SpringBootServletInitializer { +public class MultipleLoginApplication { public static void main(String[] args) { SpringApplication.run(MultipleLoginApplication.class, args); } - - @Override - protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { - return application.sources(MultipleLoginApplication.class); - } } \ No newline at end of file diff --git a/spring-security-mvc-boot/src/main/resources/spring-security-multiple-entry.xml b/spring-security-mvc-boot/src/main/resources/spring-security-multiple-entry.xml new file mode 100644 index 000000000000..c02670081049 --- /dev/null +++ b/spring-security-mvc-boot/src/main/resources/spring-security-multiple-entry.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-security-mvc-boot/src/main/resources/templates/multipleHttpElems/login.html b/spring-security-mvc-boot/src/main/resources/templates/multipleHttpElems/login.html new file mode 100644 index 000000000000..2119baec66ab --- /dev/null +++ b/spring-security-mvc-boot/src/main/resources/templates/multipleHttpElems/login.html @@ -0,0 +1,27 @@ + + + + +

Login

+ +
+ + + + + + + + + + + + + + +
Username:
Password:
+ +
+ + + \ No newline at end of file diff --git a/spring-security-mvc-boot/src/main/resources/templates/multipleHttpElems/loginWithWarning.html b/spring-security-mvc-boot/src/main/resources/templates/multipleHttpElems/loginWithWarning.html new file mode 100644 index 000000000000..a5b2eaf3dc8c --- /dev/null +++ b/spring-security-mvc-boot/src/main/resources/templates/multipleHttpElems/loginWithWarning.html @@ -0,0 +1,28 @@ + + + + +

Login

+

Warning! You are about to access sensible data!

+ +
+ + + + + + + + + + + + + + +
Username:
Password:
+ +
+ + + \ No newline at end of file diff --git a/spring-security-mvc-boot/src/main/resources/templates/multipleHttpElems/multipleHttpLinks.html b/spring-security-mvc-boot/src/main/resources/templates/multipleHttpElems/multipleHttpLinks.html new file mode 100644 index 000000000000..676badb16f03 --- /dev/null +++ b/spring-security-mvc-boot/src/main/resources/templates/multipleHttpElems/multipleHttpLinks.html @@ -0,0 +1,18 @@ + + + + +Multiple Http Elements Links + + + +Admin page +
+User page +
+Private user page +
+Guest page + + + \ No newline at end of file diff --git a/spring-security-mvc-boot/src/main/resources/templates/multipleHttpElems/myAdminPage.html b/spring-security-mvc-boot/src/main/resources/templates/multipleHttpElems/myAdminPage.html new file mode 100644 index 000000000000..3003833562d9 --- /dev/null +++ b/spring-security-mvc-boot/src/main/resources/templates/multipleHttpElems/myAdminPage.html @@ -0,0 +1,13 @@ + + + + +Admin Page + + +Welcome admin! + +

+Back to links + + \ No newline at end of file diff --git a/spring-security-mvc-boot/src/main/resources/templates/multipleHttpElems/myGuestPage.html b/spring-security-mvc-boot/src/main/resources/templates/multipleHttpElems/myGuestPage.html new file mode 100644 index 000000000000..47a4c9c44a8e --- /dev/null +++ b/spring-security-mvc-boot/src/main/resources/templates/multipleHttpElems/myGuestPage.html @@ -0,0 +1,13 @@ + + + + +Guest Page + + +Welcome guest! + +

+Back to links + + \ No newline at end of file diff --git a/spring-security-mvc-boot/src/main/resources/templates/multipleHttpElems/myPrivateUserPage.html b/spring-security-mvc-boot/src/main/resources/templates/multipleHttpElems/myPrivateUserPage.html new file mode 100644 index 000000000000..52045ec32013 --- /dev/null +++ b/spring-security-mvc-boot/src/main/resources/templates/multipleHttpElems/myPrivateUserPage.html @@ -0,0 +1,13 @@ + + + + +Insert title here + + +Welcome user to your private page! Logout + +

+Back to links + + \ No newline at end of file diff --git a/spring-security-mvc-boot/src/main/resources/templates/multipleHttpElems/myUserPage.html b/spring-security-mvc-boot/src/main/resources/templates/multipleHttpElems/myUserPage.html new file mode 100644 index 000000000000..f6c2def0b8a2 --- /dev/null +++ b/spring-security-mvc-boot/src/main/resources/templates/multipleHttpElems/myUserPage.html @@ -0,0 +1,13 @@ + + + + +User Page + + +Welcome user! Logout + +

+Back to links + + \ No newline at end of file diff --git a/spring-security-mvc-boot/src/test/java/org/baeldung/web/CustomUserDetailsServiceIntegrationTest.java b/spring-security-mvc-boot/src/test/java/org/baeldung/web/CustomUserDetailsServiceIntegrationTest.java index 616e80e6e90b..3dd4b236f937 100644 --- a/spring-security-mvc-boot/src/test/java/org/baeldung/web/CustomUserDetailsServiceIntegrationTest.java +++ b/spring-security-mvc-boot/src/test/java/org/baeldung/web/CustomUserDetailsServiceIntegrationTest.java @@ -11,7 +11,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; @@ -19,9 +18,10 @@ import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.boot.test.context.SpringBootTest; @RunWith(SpringJUnit4ClassRunner.class) -@SpringApplicationConfiguration(classes = { MvcConfig.class, SecurityConfig.class, PersistenceConfig.class }) +@SpringBootTest(classes = { MvcConfig.class, SecurityConfig.class, PersistenceConfig.class }) @WebAppConfiguration public class CustomUserDetailsServiceIntegrationTest { diff --git a/spring-security-mvc-boot/src/test/java/org/baeldung/web/MultipleEntryPointsTest.java b/spring-security-mvc-boot/src/test/java/org/baeldung/web/MultipleEntryPointsTest.java new file mode 100644 index 000000000000..050d2363afb3 --- /dev/null +++ b/spring-security-mvc-boot/src/test/java/org/baeldung/web/MultipleEntryPointsTest.java @@ -0,0 +1,64 @@ +package org.baeldung.web; + +import org.baeldung.multipleentrypoints.MultipleEntryPointsApplication; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.security.web.FilterChainProxy; +import org.springframework.test.context.junit4.SpringRunner; +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.web.context.WebApplicationContext; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*; + +@RunWith(SpringRunner.class) +@WebAppConfiguration +@SpringBootTest(classes = MultipleEntryPointsApplication.class) +public class MultipleEntryPointsTest { + @Autowired + private WebApplicationContext wac; + + @Autowired + private FilterChainProxy springSecurityFilterChain; + + private MockMvc mockMvc; + + @Before + public void setup() { + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).addFilter(springSecurityFilterChain).build(); + } + + @Test + public void whenTestAdminCredentials_thenOk() throws Exception { + mockMvc.perform(get("/admin/myAdminPage")).andExpect(status().isUnauthorized()); + + mockMvc.perform(get("/admin/myAdminPage").with(httpBasic("admin", "adminPass"))).andExpect(status().isOk()); + + mockMvc.perform(get("/user/myUserPage").with(user("admin").password("adminPass").roles("ADMIN"))).andExpect(status().isForbidden()); + + } + + @Test + public void whenTestUserCredentials_thenOk() throws Exception { + mockMvc.perform(get("/user/general/myUserPage")).andExpect(status().isFound()); + + mockMvc.perform(get("/user/general/myUserPage").with(user("user").password("userPass").roles("USER"))).andExpect(status().isOk()); + + mockMvc.perform(get("/admin/myAdminPage").with(user("user").password("userPass").roles("USER"))).andExpect(status().isForbidden()); + } + + @Test + public void givenAnyUser_whenGetGuestPage_thenOk() throws Exception { + mockMvc.perform(get("/guest/myGuestPage")).andExpect(status().isOk()); + + mockMvc.perform(get("/guest/myGuestPage").with(user("user").password("userPass").roles("USER"))).andExpect(status().isOk()); + + mockMvc.perform(get("/guest/myGuestPage").with(httpBasic("admin", "adminPass"))).andExpect(status().isOk()); + } +} diff --git a/spring-security-mvc-custom/pom.xml b/spring-security-mvc-custom/pom.xml index 59339ead5e4e..46e0dc2b17d6 100644 --- a/spring-security-mvc-custom/pom.xml +++ b/spring-security-mvc-custom/pom.xml @@ -125,11 +125,6 @@ ${org.slf4j.version}
- - org.slf4j - log4j-over-slf4j - ${org.slf4j.version} - diff --git a/spring-security-mvc-custom/src/main/resources/logback.xml b/spring-security-mvc-custom/src/main/resources/logback.xml index 1146dade632e..ec0dc2469ae0 100644 --- a/spring-security-mvc-custom/src/main/resources/logback.xml +++ b/spring-security-mvc-custom/src/main/resources/logback.xml @@ -1,20 +1,19 @@ + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - + + - - - - - - - - - + + + + + \ No newline at end of file diff --git a/spring-security-mvc-digest-auth/pom.xml b/spring-security-mvc-digest-auth/pom.xml index dd772e3343c6..c0f08c1a2a01 100644 --- a/spring-security-mvc-digest-auth/pom.xml +++ b/spring-security-mvc-digest-auth/pom.xml @@ -120,11 +120,6 @@ ${org.slf4j.version} - - org.slf4j - log4j-over-slf4j - ${org.slf4j.version} - diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/security/basic/MyBasicAuthenticationEntryPoint.java b/spring-security-mvc-digest-auth/src/main/java/org/baeldung/basic/MyBasicAuthenticationEntryPoint.java similarity index 96% rename from spring-security-basic-auth/src/main/java/org/baeldung/security/basic/MyBasicAuthenticationEntryPoint.java rename to spring-security-mvc-digest-auth/src/main/java/org/baeldung/basic/MyBasicAuthenticationEntryPoint.java index 968237227f63..c51c0a0bc8b3 100644 --- a/spring-security-basic-auth/src/main/java/org/baeldung/security/basic/MyBasicAuthenticationEntryPoint.java +++ b/spring-security-mvc-digest-auth/src/main/java/org/baeldung/basic/MyBasicAuthenticationEntryPoint.java @@ -1,4 +1,4 @@ -package org.baeldung.security.basic; +package org.baeldung.basic; import java.io.IOException; import java.io.PrintWriter; diff --git a/spring-security-mvc-digest-auth/src/main/resources/logback.xml b/spring-security-mvc-digest-auth/src/main/resources/logback.xml index 1146dade632e..ec0dc2469ae0 100644 --- a/spring-security-mvc-digest-auth/src/main/resources/logback.xml +++ b/spring-security-mvc-digest-auth/src/main/resources/logback.xml @@ -1,20 +1,19 @@ + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - + + - - - - - - - - - + + + + + \ No newline at end of file diff --git a/spring-security-mvc-ldap/src/main/resources/logback.xml b/spring-security-mvc-ldap/src/main/resources/logback.xml index 2dc76c96f305..ec0dc2469ae0 100644 --- a/spring-security-mvc-ldap/src/main/resources/logback.xml +++ b/spring-security-mvc-ldap/src/main/resources/logback.xml @@ -1,22 +1,19 @@ + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - + + - - - - - - - - - - - + + + + + \ No newline at end of file diff --git a/spring-security-mvc-login/README.md b/spring-security-mvc-login/README.md index 35305112b483..f7eb31486964 100644 --- a/spring-security-mvc-login/README.md +++ b/spring-security-mvc-login/README.md @@ -11,6 +11,7 @@ The "Learn Spring Security" Classes: http://github.learnspringsecurity.com - [Spring Security Expressions – hasRole Example](http://www.baeldung.com/spring-security-expressions-basic) - [Spring HTTP/HTTPS Channel Security](http://www.baeldung.com/spring-channel-security-https) - [Spring Security - Customize the 403 Forbidden/Access Denied Page](http://www.baeldung.com/spring-security-custom-access-denied-page) +- [Spring Security – Redirect to the Previous URL After Login](http://www.baeldung.com/spring-security-redirect-login) ### Build the Project ``` diff --git a/spring-security-mvc-login/pom.xml b/spring-security-mvc-login/pom.xml index 3809dc9f26d4..fd67873ff1b2 100644 --- a/spring-security-mvc-login/pom.xml +++ b/spring-security-mvc-login/pom.xml @@ -117,11 +117,6 @@ ${org.slf4j.version} - - org.slf4j - log4j-over-slf4j - ${org.slf4j.version} - @@ -197,14 +192,11 @@ org.apache.maven.plugins maven-surefire-plugin - ${maven-surefire-plugin.version} - + **/*IntegrationTest.java + **/*LiveTest.java - - - @@ -268,7 +260,6 @@ 3.6.0 2.6 - 2.19.1 2.7 1.6.1 diff --git a/spring-security-mvc-login/src/main/java/org/baeldung/security/CustomAccessDeniedHandler.java b/spring-security-mvc-login/src/main/java/org/baeldung/security/CustomAccessDeniedHandler.java index ea4407c5c48f..843f5f4dcd97 100644 --- a/spring-security-mvc-login/src/main/java/org/baeldung/security/CustomAccessDeniedHandler.java +++ b/spring-security-mvc-login/src/main/java/org/baeldung/security/CustomAccessDeniedHandler.java @@ -6,7 +6,9 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.apache.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import org.springframework.security.access.AccessDeniedException; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; @@ -14,7 +16,7 @@ public class CustomAccessDeniedHandler implements AccessDeniedHandler { - public static final Logger LOG = Logger.getLogger(CustomAccessDeniedHandler.class); + public static final Logger LOG = LoggerFactory.getLogger(CustomAccessDeniedHandler.class); @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException exc) throws IOException, ServletException { diff --git a/spring-security-mvc-login/src/main/resources/logback.xml b/spring-security-mvc-login/src/main/resources/logback.xml index 1146dade632e..ec0dc2469ae0 100644 --- a/spring-security-mvc-login/src/main/resources/logback.xml +++ b/spring-security-mvc-login/src/main/resources/logback.xml @@ -1,20 +1,19 @@ + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - + + - - - - - - - - - + + + + + \ No newline at end of file diff --git a/spring-security-mvc-persisted-remember-me/pom.xml b/spring-security-mvc-persisted-remember-me/pom.xml index f215c39c6dde..7fc15284f078 100644 --- a/spring-security-mvc-persisted-remember-me/pom.xml +++ b/spring-security-mvc-persisted-remember-me/pom.xml @@ -155,11 +155,6 @@ ${org.slf4j.version} - - org.slf4j - log4j-over-slf4j - ${org.slf4j.version} - diff --git a/spring-security-mvc-persisted-remember-me/src/main/resources/logback.xml b/spring-security-mvc-persisted-remember-me/src/main/resources/logback.xml index 1146dade632e..ec0dc2469ae0 100644 --- a/spring-security-mvc-persisted-remember-me/src/main/resources/logback.xml +++ b/spring-security-mvc-persisted-remember-me/src/main/resources/logback.xml @@ -1,20 +1,19 @@ + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - + + - - - - - - - - - + + + + + \ No newline at end of file diff --git a/spring-security-mvc-session/pom.xml b/spring-security-mvc-session/pom.xml index 3f129c83babb..5255bd3737a9 100644 --- a/spring-security-mvc-session/pom.xml +++ b/spring-security-mvc-session/pom.xml @@ -125,11 +125,6 @@ ${org.slf4j.version} - - org.slf4j - log4j-over-slf4j - ${org.slf4j.version} - diff --git a/spring-security-mvc-session/src/main/resources/logback.xml b/spring-security-mvc-session/src/main/resources/logback.xml index 1146dade632e..ec0dc2469ae0 100644 --- a/spring-security-mvc-session/src/main/resources/logback.xml +++ b/spring-security-mvc-session/src/main/resources/logback.xml @@ -1,20 +1,19 @@ + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - + + - - - - - - - - - + + + + + \ No newline at end of file diff --git a/spring-security-openid/README.md b/spring-security-openid/README.md index 79bf44f374c5..8c65c09f2f50 100644 --- a/spring-security-openid/README.md +++ b/spring-security-openid/README.md @@ -1,4 +1,7 @@ -========= +### Relevant articles + +- [Spring Security and OpenID Connect](http://www.baeldung.com/spring-security-openid-connect) + ## OpenID Connect with Spring Security @@ -13,3 +16,4 @@ mvn spring-boot:run - Make sure you set redirect URI to http://localhost:8081/google-login - Once you have your client id and secret, make sure you add them to the `application.properties` of the project + diff --git a/spring-security-openid/pom.xml b/spring-security-openid/pom.xml index ff0b030bd260..72cc63474b86 100644 --- a/spring-security-openid/pom.xml +++ b/spring-security-openid/pom.xml @@ -13,7 +13,7 @@ org.springframework.boot spring-boot-starter-parent - 1.4.4.RELEASE + 1.5.2.RELEASE diff --git a/spring-security-rest-basic-auth/README.md b/spring-security-rest-basic-auth/README.md index 3bd46bdd2aef..43ab08b8ca6a 100644 --- a/spring-security-rest-basic-auth/README.md +++ b/spring-security-rest-basic-auth/README.md @@ -9,3 +9,5 @@ The "Learn Spring Security" Classes: http://github.learnspringsecurity.com - [RestTemplate with Basic Authentication in Spring](http://www.baeldung.com/2012/04/16/how-to-use-resttemplate-with-basic-authentication-in-spring-3-1) - [HttpClient Timeout](http://www.baeldung.com/httpclient-timeout) - [HttpClient with SSL](http://www.baeldung.com/httpclient-ssl) +- [Writing a Custom Filter in Spring Security](http://www.baeldung.com/spring-security-custom-filter) +- [Spring Security Basic Authentication](http://www.baeldung.com/spring-security-basic-authentication) \ No newline at end of file diff --git a/spring-security-rest-basic-auth/pom.xml b/spring-security-rest-basic-auth/pom.xml index 953cfd6b6bc5..0be9b664e20b 100644 --- a/spring-security-rest-basic-auth/pom.xml +++ b/spring-security-rest-basic-auth/pom.xml @@ -172,11 +172,6 @@ ${org.slf4j.version} - - org.slf4j - log4j-over-slf4j - ${org.slf4j.version} - diff --git a/spring-security-mvc-digest-auth/src/main/java/org/baeldung/security/basic/MyBasicAuthenticationEntryPoint.java b/spring-security-rest-basic-auth/src/main/java/org/baeldung/basic/MyBasicAuthenticationEntryPoint.java similarity index 96% rename from spring-security-mvc-digest-auth/src/main/java/org/baeldung/security/basic/MyBasicAuthenticationEntryPoint.java rename to spring-security-rest-basic-auth/src/main/java/org/baeldung/basic/MyBasicAuthenticationEntryPoint.java index 968237227f63..6e580e7a22b9 100644 --- a/spring-security-mvc-digest-auth/src/main/java/org/baeldung/security/basic/MyBasicAuthenticationEntryPoint.java +++ b/spring-security-rest-basic-auth/src/main/java/org/baeldung/basic/MyBasicAuthenticationEntryPoint.java @@ -1,15 +1,14 @@ -package org.baeldung.security.basic; +package org.baeldung.basic; -import java.io.IOException; -import java.io.PrintWriter; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint; +import org.springframework.stereotype.Component; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; - -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint; -import org.springframework.stereotype.Component; +import java.io.IOException; +import java.io.PrintWriter; @Component public class MyBasicAuthenticationEntryPoint extends BasicAuthenticationEntryPoint { diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/security/filter/CustomFilter.java b/spring-security-rest-basic-auth/src/main/java/org/baeldung/filter/CustomFilter.java similarity index 92% rename from spring-security-basic-auth/src/main/java/org/baeldung/security/filter/CustomFilter.java rename to spring-security-rest-basic-auth/src/main/java/org/baeldung/filter/CustomFilter.java index 8d2b919cb0e4..01e5b0b59dce 100644 --- a/spring-security-basic-auth/src/main/java/org/baeldung/security/filter/CustomFilter.java +++ b/spring-security-rest-basic-auth/src/main/java/org/baeldung/filter/CustomFilter.java @@ -1,4 +1,4 @@ -package org.baeldung.security.filter; +package org.baeldung.filter; import org.springframework.web.filter.GenericFilterBean; diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/security/filter/configuration/CustomWebSecurityConfigurerAdapter.java b/spring-security-rest-basic-auth/src/main/java/org/baeldung/filter/CustomWebSecurityConfigurerAdapter.java similarity index 63% rename from spring-security-basic-auth/src/main/java/org/baeldung/security/filter/configuration/CustomWebSecurityConfigurerAdapter.java rename to spring-security-rest-basic-auth/src/main/java/org/baeldung/filter/CustomWebSecurityConfigurerAdapter.java index d03d9cc018cd..2ff0e30f945d 100644 --- a/spring-security-basic-auth/src/main/java/org/baeldung/security/filter/configuration/CustomWebSecurityConfigurerAdapter.java +++ b/spring-security-rest-basic-auth/src/main/java/org/baeldung/filter/CustomWebSecurityConfigurerAdapter.java @@ -1,7 +1,6 @@ -package org.baeldung.security.filter.configuration; +package org.baeldung.filter; -import org.baeldung.security.basic.MyBasicAuthenticationEntryPoint; -import org.baeldung.security.filter.CustomFilter; +import org.baeldung.security.RestAuthenticationEntryPoint; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; @@ -14,17 +13,28 @@ @EnableWebSecurity public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter { - @Autowired - private MyBasicAuthenticationEntryPoint authenticationEntryPoint; + @Autowired private RestAuthenticationEntryPoint authenticationEntryPoint; @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - auth.inMemoryAuthentication().withUser("user1").password("user1Pass").authorities("ROLE_USER"); + auth + .inMemoryAuthentication() + .withUser("user1") + .password("user1Pass") + .authorities("ROLE_USER"); } @Override protected void configure(HttpSecurity http) throws Exception { - http.authorizeRequests().antMatchers("/securityNone").permitAll().anyRequest().authenticated().and().httpBasic().authenticationEntryPoint(authenticationEntryPoint); + http + .authorizeRequests() + .antMatchers("/securityNone") + .permitAll() + .anyRequest() + .authenticated() + .and() + .httpBasic() + .authenticationEntryPoint(authenticationEntryPoint); http.addFilterAfter(new CustomFilter(), BasicAuthenticationFilter.class); } diff --git a/spring-security-rest-basic-auth/src/main/resources/logback.xml b/spring-security-rest-basic-auth/src/main/resources/logback.xml index 90f61a95bc55..ec0dc2469ae0 100644 --- a/spring-security-rest-basic-auth/src/main/resources/logback.xml +++ b/spring-security-rest-basic-auth/src/main/resources/logback.xml @@ -1,22 +1,19 @@ + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - + + - - - - - - - - - - - + + + + + \ No newline at end of file diff --git a/spring-security-rest-custom/pom.xml b/spring-security-rest-custom/pom.xml index d421f8d7b49a..0fe642c2b630 100644 --- a/spring-security-rest-custom/pom.xml +++ b/spring-security-rest-custom/pom.xml @@ -177,10 +177,6 @@ jcl-over-slf4j - - org.slf4j - log4j-over-slf4j - diff --git a/spring-security-rest-custom/src/main/resources/logback.xml b/spring-security-rest-custom/src/main/resources/logback.xml index 1146dade632e..ec0dc2469ae0 100644 --- a/spring-security-rest-custom/src/main/resources/logback.xml +++ b/spring-security-rest-custom/src/main/resources/logback.xml @@ -1,20 +1,19 @@ + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - + + - - - - - - - - - + + + + + \ No newline at end of file diff --git a/spring-security-rest-digest-auth/pom.xml b/spring-security-rest-digest-auth/pom.xml index dc8e12b72b16..e2fcd330f574 100644 --- a/spring-security-rest-digest-auth/pom.xml +++ b/spring-security-rest-digest-auth/pom.xml @@ -166,11 +166,6 @@ ${org.slf4j.version} - - org.slf4j - log4j-over-slf4j - ${org.slf4j.version} - diff --git a/spring-security-rest-digest-auth/src/main/resources/logback.xml b/spring-security-rest-digest-auth/src/main/resources/logback.xml index 90f61a95bc55..ec0dc2469ae0 100644 --- a/spring-security-rest-digest-auth/src/main/resources/logback.xml +++ b/spring-security-rest-digest-auth/src/main/resources/logback.xml @@ -1,22 +1,19 @@ + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - + + - - - - - - - - - - - + + + + + \ No newline at end of file diff --git a/spring-security-rest-full/README.md b/spring-security-rest-full/README.md index faeeac1ec29c..d5bd8be52b93 100644 --- a/spring-security-rest-full/README.md +++ b/spring-security-rest-full/README.md @@ -29,6 +29,11 @@ The "Learn Spring Security" Classes: http://github.learnspringsecurity.com - [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) + + + ### Build the Project ``` diff --git a/spring-security-rest-full/pom.xml b/spring-security-rest-full/pom.xml index 43d14af5ea0c..290aa48deac3 100644 --- a/spring-security-rest-full/pom.xml +++ b/spring-security-rest-full/pom.xml @@ -226,10 +226,6 @@ jcl-over-slf4j - - org.slf4j - log4j-over-slf4j - diff --git a/spring-security-rest-full/src/main/java/org/baeldung/persistence/dao/GenericSpecificationsBuilder.java b/spring-security-rest-full/src/main/java/org/baeldung/persistence/dao/GenericSpecificationsBuilder.java new file mode 100644 index 000000000000..64bab9a4357c --- /dev/null +++ b/spring-security-rest-full/src/main/java/org/baeldung/persistence/dao/GenericSpecificationsBuilder.java @@ -0,0 +1,99 @@ +package org.baeldung.persistence.dao; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Deque; +import java.util.LinkedList; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.baeldung.web.util.SearchOperation; +import org.baeldung.web.util.SpecSearchCriteria; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.jpa.domain.Specifications; + +public class GenericSpecificationsBuilder { + + private final List params; + + public GenericSpecificationsBuilder() { + this.params = new ArrayList<>(); + } + + public final GenericSpecificationsBuilder with(final String key, final String operation, final Object value, final String prefix, final String suffix) { + return with(null, key, operation, value, prefix, suffix); + } + + public final GenericSpecificationsBuilder with(final String precedenceIndicator, final String key, final String operation, final Object value, final String prefix, final String suffix) { + SearchOperation op = SearchOperation.getSimpleOperation(operation.charAt(0)); + if (op != null) { + if (op == SearchOperation.EQUALITY) // the operation may be complex operation + { + final boolean startWithAsterisk = prefix != null && prefix.contains(SearchOperation.ZERO_OR_MORE_REGEX); + final boolean endWithAsterisk = suffix != null && suffix.contains(SearchOperation.ZERO_OR_MORE_REGEX); + + if (startWithAsterisk && endWithAsterisk) { + op = SearchOperation.CONTAINS; + } else if (startWithAsterisk) { + op = SearchOperation.ENDS_WITH; + } else if (endWithAsterisk) { + op = SearchOperation.STARTS_WITH; + } + } + params.add(new SpecSearchCriteria(precedenceIndicator, key, op, value)); + } + return this; + } + + public Specification build(Function> converter) { + + if (params.size() == 0) { + return null; + } + + final List> specs = params.stream() + .map(converter) + .collect(Collectors.toCollection(ArrayList::new)); + + Specification result = specs.get(0); + + for (int idx = 1; idx < specs.size(); idx++) { + result = params.get(idx) + .isOrPredicate() + ? Specifications.where(result) + .or(specs.get(idx)) + : Specifications.where(result) + .and(specs.get(idx)); + } + return result; + } + + public Specification build(Deque postFixedExprStack, Function> converter) { + + Deque> specStack = new LinkedList<>(); + + Collections.reverse((List) postFixedExprStack); + + while (!postFixedExprStack.isEmpty()) { + Object mayBeOperand = postFixedExprStack.pop(); + + if (!(mayBeOperand instanceof String)) { + specStack.push(converter.apply((SpecSearchCriteria) mayBeOperand)); + } else { + Specification operand1 = specStack.pop(); + Specification operand2 = specStack.pop(); + if (mayBeOperand.equals(SearchOperation.AND_OPERATOR)) + specStack.push(Specifications.where(operand1) + .and(operand2)); + else if (mayBeOperand.equals(SearchOperation.OR_OPERATOR)) + specStack.push(Specifications.where(operand1) + .or(operand2)); + } + + } + return specStack.pop(); + + } + +} diff --git a/spring-security-rest-full/src/main/java/org/baeldung/persistence/dao/UserSpecification.java b/spring-security-rest-full/src/main/java/org/baeldung/persistence/dao/UserSpecification.java index e41c7ca6636e..2788c46fde5e 100644 --- a/spring-security-rest-full/src/main/java/org/baeldung/persistence/dao/UserSpecification.java +++ b/spring-security-rest-full/src/main/java/org/baeldung/persistence/dao/UserSpecification.java @@ -11,39 +11,39 @@ public class UserSpecification implements Specification { - private SpecSearchCriteria criteria; - - public UserSpecification(final SpecSearchCriteria criteria) { - super(); - this.criteria = criteria; - } - - public SpecSearchCriteria getCriteria() { - return criteria; - } - - @Override - public Predicate toPredicate(final Root root, final CriteriaQuery query, final CriteriaBuilder builder) { - switch (criteria.getOperation()) { - case EQUALITY: - return builder.equal(root.get(criteria.getKey()), criteria.getValue()); - case NEGATION: - return builder.notEqual(root.get(criteria.getKey()), criteria.getValue()); - case GREATER_THAN: - return builder.greaterThan(root. get(criteria.getKey()), criteria.getValue().toString()); - case LESS_THAN: - return builder.lessThan(root. get(criteria.getKey()), criteria.getValue().toString()); - case LIKE: - return builder.like(root. get(criteria.getKey()), criteria.getValue().toString()); - case STARTS_WITH: - return builder.like(root. get(criteria.getKey()), criteria.getValue() + "%"); - case ENDS_WITH: - return builder.like(root. get(criteria.getKey()), "%" + criteria.getValue()); - case CONTAINS: - return builder.like(root. get(criteria.getKey()), "%" + criteria.getValue() + "%"); - default: - return null; - } - } + private SpecSearchCriteria criteria; + + public UserSpecification(final SpecSearchCriteria criteria) { + super(); + this.criteria = criteria; + } + + public SpecSearchCriteria getCriteria() { + return criteria; + } + + @Override + public Predicate toPredicate(final Root root, final CriteriaQuery query, final CriteriaBuilder builder) { + switch (criteria.getOperation()) { + case EQUALITY: + return builder.equal(root.get(criteria.getKey()), criteria.getValue()); + case NEGATION: + return builder.notEqual(root.get(criteria.getKey()), criteria.getValue()); + case GREATER_THAN: + return builder.greaterThan(root. get(criteria.getKey()), criteria.getValue().toString()); + case LESS_THAN: + return builder.lessThan(root. get(criteria.getKey()), criteria.getValue().toString()); + case LIKE: + return builder.like(root. get(criteria.getKey()), criteria.getValue().toString()); + case STARTS_WITH: + return builder.like(root. get(criteria.getKey()), criteria.getValue() + "%"); + case ENDS_WITH: + return builder.like(root. get(criteria.getKey()), "%" + criteria.getValue()); + case CONTAINS: + return builder.like(root. get(criteria.getKey()), "%" + criteria.getValue() + "%"); + default: + return null; + } + } } diff --git a/spring-security-rest-full/src/main/java/org/baeldung/persistence/dao/UserSpecificationsBuilder.java b/spring-security-rest-full/src/main/java/org/baeldung/persistence/dao/UserSpecificationsBuilder.java index 3db4267ae0d4..a8e5b96acb9c 100644 --- a/spring-security-rest-full/src/main/java/org/baeldung/persistence/dao/UserSpecificationsBuilder.java +++ b/spring-security-rest-full/src/main/java/org/baeldung/persistence/dao/UserSpecificationsBuilder.java @@ -1,60 +1,75 @@ -package org.baeldung.persistence.dao; - -import java.util.ArrayList; -import java.util.List; - -import org.baeldung.persistence.model.User; -import org.baeldung.web.util.SearchOperation; -import org.baeldung.web.util.SpecSearchCriteria; -import org.springframework.data.jpa.domain.Specification; -import org.springframework.data.jpa.domain.Specifications; - -public final class UserSpecificationsBuilder { - - private final List params; - - public UserSpecificationsBuilder() { - params = new ArrayList(); - } - - // API - - public final UserSpecificationsBuilder with(final String key, final String operation, final Object value, final String prefix, final String suffix) { - SearchOperation op = SearchOperation.getSimpleOperation(operation.charAt(0)); - if (op != null) { - if (op == SearchOperation.EQUALITY) // the operation may be complex operation - { - final boolean startWithAsterisk = prefix.contains("*"); - final boolean endWithAsterisk = suffix.contains("*"); - - if (startWithAsterisk && endWithAsterisk) { - op = SearchOperation.CONTAINS; - } else if (startWithAsterisk) { - op = SearchOperation.ENDS_WITH; - } else if (endWithAsterisk) { - op = SearchOperation.STARTS_WITH; - } - } - params.add(new SpecSearchCriteria(key, op, value)); - } - return this; - } - - public Specification build() { - if (params.size() == 0) { - return null; - } - - final List> specs = new ArrayList>(); - for (final SpecSearchCriteria param : params) { - specs.add(new UserSpecification(param)); - } - - Specification result = specs.get(0); - for (int i = 1; i < specs.size(); i++) { - result = Specifications.where(result).and(specs.get(i)); - } - return result; - } - -} +package org.baeldung.persistence.dao; + +import java.util.ArrayList; +import java.util.List; + +import org.baeldung.persistence.model.User; +import org.baeldung.web.util.SearchOperation; +import org.baeldung.web.util.SpecSearchCriteria; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.jpa.domain.Specifications; + +public final class UserSpecificationsBuilder { + + private final List params; + + public UserSpecificationsBuilder() { + params = new ArrayList(); + } + + // API + + public final UserSpecificationsBuilder with(final String key, final String operation, final Object value, final String prefix, final String suffix) { + return with(null, key, operation, value, prefix, suffix); + } + + public final UserSpecificationsBuilder with(final String orPredicate, final String key, final String operation, final Object value, final String prefix, final String suffix) { + SearchOperation op = SearchOperation.getSimpleOperation(operation.charAt(0)); + if (op != null) { + if (op == SearchOperation.EQUALITY) { // the operation may be complex operation + final boolean startWithAsterisk = prefix != null && prefix.contains(SearchOperation.ZERO_OR_MORE_REGEX); + final boolean endWithAsterisk = suffix != null && suffix.contains(SearchOperation.ZERO_OR_MORE_REGEX); + + if (startWithAsterisk && endWithAsterisk) { + op = SearchOperation.CONTAINS; + } else if (startWithAsterisk) { + op = SearchOperation.ENDS_WITH; + } else if (endWithAsterisk) { + op = SearchOperation.STARTS_WITH; + } + } + params.add(new SpecSearchCriteria(orPredicate, key, op, value)); + } + return this; + } + + public Specification build() { + + if (params.size() == 0) + return null; + + Specification result = new UserSpecification(params.get(0)); + + for (int i = 1; i < params.size(); i++) { + result = params.get(i) + .isOrPredicate() + ? Specifications.where(result) + .or(new UserSpecification(params.get(i))) + : Specifications.where(result) + .and(new UserSpecification(params.get(i))); + + } + + return result; + } + + public final UserSpecificationsBuilder with(UserSpecification spec) { + params.add(spec.getCriteria()); + return this; + } + + public final UserSpecificationsBuilder with(SpecSearchCriteria criteria) { + params.add(criteria); + return this; + } +} diff --git a/spring-security-rest-full/src/main/java/org/baeldung/web/controller/UserController.java b/spring-security-rest-full/src/main/java/org/baeldung/web/controller/UserController.java index d20423ddc013..8953a52a1bf4 100644 --- a/spring-security-rest-full/src/main/java/org/baeldung/web/controller/UserController.java +++ b/spring-security-rest-full/src/main/java/org/baeldung/web/controller/UserController.java @@ -1,133 +1,171 @@ -package org.baeldung.web.controller; - -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.baeldung.persistence.dao.IUserDAO; -import org.baeldung.persistence.dao.MyUserPredicatesBuilder; -import org.baeldung.persistence.dao.MyUserRepository; -import org.baeldung.persistence.dao.UserRepository; -import org.baeldung.persistence.dao.UserSpecificationsBuilder; -import org.baeldung.persistence.dao.rsql.CustomRsqlVisitor; -import org.baeldung.persistence.model.MyUser; -import org.baeldung.persistence.model.User; -import org.baeldung.web.util.SearchCriteria; -import org.baeldung.web.util.SearchOperation; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.jpa.domain.Specification; -import org.springframework.data.querydsl.binding.QuerydslPredicate; -import org.springframework.http.HttpStatus; -import org.springframework.stereotype.Controller; -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.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.ResponseStatus; - -import com.google.common.base.Joiner; -import com.google.common.base.Preconditions; -import com.querydsl.core.types.Predicate; -import com.querydsl.core.types.dsl.BooleanExpression; - -import cz.jirutka.rsql.parser.RSQLParser; -import cz.jirutka.rsql.parser.ast.Node; - -//@EnableSpringDataWebSupport -@Controller -@RequestMapping(value = "/auth/") -public class UserController { - - @Autowired - private IUserDAO service; - - @Autowired - private UserRepository dao; - - @Autowired - private MyUserRepository myUserRepository; - - public UserController() { - super(); - } - - // API - READ - - @RequestMapping(method = RequestMethod.GET, value = "/users") - @ResponseBody - public List findAll(@RequestParam(value = "search", required = false) final String search) { - final List params = new ArrayList(); - if (search != null) { - final Pattern pattern = Pattern.compile("(\\w+?)(:|<|>)(\\w+?),"); - final Matcher matcher = pattern.matcher(search + ","); - while (matcher.find()) { - params.add(new SearchCriteria(matcher.group(1), matcher.group(2), matcher.group(3))); - } - } - return service.searchUser(params); - } - - @RequestMapping(method = RequestMethod.GET, value = "/users/spec") - @ResponseBody - public List findAllBySpecification(@RequestParam(value = "search") final String search) { - final UserSpecificationsBuilder builder = new UserSpecificationsBuilder(); - final String operationSetExper = Joiner.on("|").join(SearchOperation.SIMPLE_OPERATION_SET); - final Pattern pattern = Pattern.compile("(\\w+?)(" + operationSetExper + ")(\\p{Punct}?)(\\w+?)(\\p{Punct}?),"); - final Matcher matcher = pattern.matcher(search + ","); - while (matcher.find()) { - builder.with(matcher.group(1), matcher.group(2), matcher.group(4), matcher.group(3), matcher.group(5)); - } - - final Specification spec = builder.build(); - return dao.findAll(spec); - } - - @RequestMapping(method = RequestMethod.GET, value = "/myusers") - @ResponseBody - public Iterable findAllByQuerydsl(@RequestParam(value = "search") final String search) { - final MyUserPredicatesBuilder builder = new MyUserPredicatesBuilder(); - if (search != null) { - final Pattern pattern = Pattern.compile("(\\w+?)(:|<|>)(\\w+?),"); - final Matcher matcher = pattern.matcher(search + ","); - while (matcher.find()) { - builder.with(matcher.group(1), matcher.group(2), matcher.group(3)); - } - } - final BooleanExpression exp = builder.build(); - return myUserRepository.findAll(exp); - } - - @RequestMapping(method = RequestMethod.GET, value = "/users/rsql") - @ResponseBody - public List findAllByRsql(@RequestParam(value = "search") final String search) { - final Node rootNode = new RSQLParser().parse(search); - final Specification spec = rootNode.accept(new CustomRsqlVisitor()); - return dao.findAll(spec); - } - - @RequestMapping(method = RequestMethod.GET, value = "/api/myusers") - @ResponseBody - public Iterable findAllByWebQuerydsl(@QuerydslPredicate(root = MyUser.class) final Predicate predicate) { - return myUserRepository.findAll(predicate); - } - - // API - WRITE - - @RequestMapping(method = RequestMethod.POST, value = "/users") - @ResponseStatus(HttpStatus.CREATED) - public void create(@RequestBody final User resource) { - Preconditions.checkNotNull(resource); - dao.save(resource); - } - - @RequestMapping(method = RequestMethod.POST, value = "/myusers") - @ResponseStatus(HttpStatus.CREATED) - public void addMyUser(@RequestBody final MyUser resource) { - Preconditions.checkNotNull(resource); - myUserRepository.save(resource); - - } - -} +package org.baeldung.web.controller; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.baeldung.persistence.dao.GenericSpecificationsBuilder; +import org.baeldung.persistence.dao.IUserDAO; +import org.baeldung.persistence.dao.MyUserPredicatesBuilder; +import org.baeldung.persistence.dao.MyUserRepository; +import org.baeldung.persistence.dao.UserRepository; +import org.baeldung.persistence.dao.UserSpecification; +import org.baeldung.persistence.dao.UserSpecificationsBuilder; +import org.baeldung.persistence.dao.rsql.CustomRsqlVisitor; +import org.baeldung.persistence.model.MyUser; +import org.baeldung.persistence.model.User; +import org.baeldung.web.util.CriteriaParser; +import org.baeldung.web.util.SearchCriteria; +import org.baeldung.web.util.SearchOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.querydsl.binding.QuerydslPredicate; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +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.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; + +import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; +import com.querydsl.core.types.Predicate; +import com.querydsl.core.types.dsl.BooleanExpression; + +import cz.jirutka.rsql.parser.RSQLParser; +import cz.jirutka.rsql.parser.ast.Node; + +//@EnableSpringDataWebSupport +@Controller +@RequestMapping(value = "/auth/") +public class UserController { + + @Autowired + private IUserDAO service; + + @Autowired + private UserRepository dao; + + @Autowired + private MyUserRepository myUserRepository; + + public UserController() { + super(); + } + + // API - READ + + @RequestMapping(method = RequestMethod.GET, value = "/users") + @ResponseBody + public List findAll(@RequestParam(value = "search", required = false) String search) { + List params = new ArrayList(); + if (search != null) { + Pattern pattern = Pattern.compile("(\\w+?)(:|<|>)(\\w+?),"); + Matcher matcher = pattern.matcher(search + ","); + while (matcher.find()) { + params.add(new SearchCriteria(matcher.group(1), matcher.group(2), matcher.group(3))); + } + } + return service.searchUser(params); + } + + @RequestMapping(method = RequestMethod.GET, value = "/users/spec") + @ResponseBody + public List findAllBySpecification(@RequestParam(value = "search") String search) { + UserSpecificationsBuilder builder = new UserSpecificationsBuilder(); + String operationSetExper = Joiner.on("|") + .join(SearchOperation.SIMPLE_OPERATION_SET); + Pattern pattern = Pattern.compile("(\\w+?)(" + operationSetExper + ")(\\p{Punct}?)(\\w+?)(\\p{Punct}?),"); + Matcher matcher = pattern.matcher(search + ","); + while (matcher.find()) { + builder.with(matcher.group(1), matcher.group(2), matcher.group(4), matcher.group(3), matcher.group(5)); + } + + Specification spec = builder.build(); + return dao.findAll(spec); + } + + @GetMapping(value = "/users/espec") + @ResponseBody + public List findAllByOrPredicate(@RequestParam(value = "search") String search) { + Specification spec = resolveSpecification(search); + return dao.findAll(spec); + } + + @GetMapping(value = "/users/spec/adv") + @ResponseBody + public List findAllByAdvPredicate(@RequestParam(value = "search") String search) { + Specification spec = resolveSpecificationFromInfixExpr(search); + return dao.findAll(spec); + } + + protected Specification resolveSpecificationFromInfixExpr(String searchParameters) { + CriteriaParser parser = new CriteriaParser(); + GenericSpecificationsBuilder specBuilder = new GenericSpecificationsBuilder<>(); + return specBuilder.build(parser.parse(searchParameters), UserSpecification::new); + } + + protected Specification resolveSpecification(String searchParameters) { + + UserSpecificationsBuilder builder = new UserSpecificationsBuilder(); + String operationSetExper = Joiner.on("|") + .join(SearchOperation.SIMPLE_OPERATION_SET); + Pattern pattern = Pattern.compile("(\\p{Punct}?)(\\w+?)(" + operationSetExper + ")(\\p{Punct}?)(\\w+?)(\\p{Punct}?),"); + Matcher matcher = pattern.matcher(searchParameters + ","); + while (matcher.find()) { + builder.with(matcher.group(1), matcher.group(2), matcher.group(3), matcher.group(5), matcher.group(4), matcher.group(6)); + } + return builder.build(); + } + + @RequestMapping(method = RequestMethod.GET, value = "/myusers") + @ResponseBody + public Iterable findAllByQuerydsl(@RequestParam(value = "search") String search) { + MyUserPredicatesBuilder builder = new MyUserPredicatesBuilder(); + if (search != null) { + Pattern pattern = Pattern.compile("(\\w+?)(:|<|>)(\\w+?),"); + Matcher matcher = pattern.matcher(search + ","); + while (matcher.find()) { + builder.with(matcher.group(1), matcher.group(2), matcher.group(3)); + } + } + BooleanExpression exp = builder.build(); + return myUserRepository.findAll(exp); + } + + @RequestMapping(method = RequestMethod.GET, value = "/users/rsql") + @ResponseBody + public List findAllByRsql(@RequestParam(value = "search") String search) { + Node rootNode = new RSQLParser().parse(search); + Specification spec = rootNode.accept(new CustomRsqlVisitor()); + return dao.findAll(spec); + } + + @RequestMapping(method = RequestMethod.GET, value = "/api/myusers") + @ResponseBody + public Iterable findAllByWebQuerydsl(@QuerydslPredicate(root = MyUser.class) Predicate predicate) { + return myUserRepository.findAll(predicate); + } + + // API - WRITE + + @RequestMapping(method = RequestMethod.POST, value = "/users") + @ResponseStatus(HttpStatus.CREATED) + public void create(@RequestBody User resource) { + Preconditions.checkNotNull(resource); + dao.save(resource); + } + + @RequestMapping(method = RequestMethod.POST, value = "/myusers") + @ResponseStatus(HttpStatus.CREATED) + public void addMyUser(@RequestBody MyUser resource) { + Preconditions.checkNotNull(resource); + myUserRepository.save(resource); + + } + +} diff --git a/spring-security-rest-full/src/main/java/org/baeldung/web/util/CriteriaParser.java b/spring-security-rest-full/src/main/java/org/baeldung/web/util/CriteriaParser.java new file mode 100644 index 000000000000..eabc938bce56 --- /dev/null +++ b/spring-security-rest-full/src/main/java/org/baeldung/web/util/CriteriaParser.java @@ -0,0 +1,76 @@ +package org.baeldung.web.util; + +import java.util.Collections; +import java.util.Deque; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.google.common.base.Joiner; + +public class CriteriaParser { + + private static Map ops; + + private static Pattern SpecCriteraRegex = Pattern.compile("^(\\w+?)(" + Joiner.on("|") + .join(SearchOperation.SIMPLE_OPERATION_SET) + ")(\\p{Punct}?)(\\w+?)(\\p{Punct}?)$"); + + private enum Operator { + OR(1), AND(2); + final int precedence; + + Operator(int p) { + precedence = p; + } + } + + static { + Map tempMap = new HashMap<>(); + tempMap.put("AND", Operator.AND); + tempMap.put("OR", Operator.OR); + tempMap.put("or", Operator.OR); + tempMap.put("and", Operator.AND); + + ops = Collections.unmodifiableMap(tempMap); + } + + private static boolean isHigerPrecedenceOperator(String currOp, String prevOp) { + return (ops.containsKey(prevOp) && ops.get(prevOp).precedence >= ops.get(currOp).precedence); + } + + public Deque parse(String searchParam) { + + Deque output = new LinkedList<>(); + Deque stack = new LinkedList<>(); + + for (String token : searchParam.split("\\s+")) { + if (ops.containsKey(token)) { + while (!stack.isEmpty() && isHigerPrecedenceOperator(token, stack.peek())) + output.push(stack.pop() + .equalsIgnoreCase(SearchOperation.OR_OPERATOR) ? SearchOperation.OR_OPERATOR : SearchOperation.AND_OPERATOR); + stack.push(token.equalsIgnoreCase(SearchOperation.OR_OPERATOR) ? SearchOperation.OR_OPERATOR : SearchOperation.AND_OPERATOR); + } else if (token.equals(SearchOperation.LEFT_PARANTHESIS)) { + stack.push(SearchOperation.LEFT_PARANTHESIS); + } else if (token.equals(SearchOperation.RIGHT_PARANTHESIS)) { + while (!stack.peek() + .equals(SearchOperation.LEFT_PARANTHESIS)) + output.push(stack.pop()); + stack.pop(); + } else { + + Matcher matcher = SpecCriteraRegex.matcher(token); + while (matcher.find()) { + output.push(new SpecSearchCriteria(matcher.group(1), matcher.group(2), matcher.group(3), matcher.group(4), matcher.group(5))); + } + } + } + + while (!stack.isEmpty()) + output.push(stack.pop()); + + return output; + } + +} diff --git a/spring-security-rest-full/src/main/java/org/baeldung/web/util/SearchOperation.java b/spring-security-rest-full/src/main/java/org/baeldung/web/util/SearchOperation.java index 703f9b93f62b..db2c0133cfa2 100644 --- a/spring-security-rest-full/src/main/java/org/baeldung/web/util/SearchOperation.java +++ b/spring-security-rest-full/src/main/java/org/baeldung/web/util/SearchOperation.java @@ -1,24 +1,36 @@ -package org.baeldung.web.util; - -public enum SearchOperation { - EQUALITY, NEGATION, GREATER_THAN, LESS_THAN, LIKE, STARTS_WITH, ENDS_WITH, CONTAINS; - - public static final String[] SIMPLE_OPERATION_SET = { ":", "!", ">", "<", "~" }; - - public static SearchOperation getSimpleOperation(final char input) { - switch (input) { - case ':': - return EQUALITY; - case '!': - return NEGATION; - case '>': - return GREATER_THAN; - case '<': - return LESS_THAN; - case '~': - return LIKE; - default: - return null; - } - } -} +package org.baeldung.web.util; + +public enum SearchOperation { + EQUALITY, NEGATION, GREATER_THAN, LESS_THAN, LIKE, STARTS_WITH, ENDS_WITH, CONTAINS; + + public static final String[] SIMPLE_OPERATION_SET = { ":", "!", ">", "<", "~" }; + + public static final String OR_PREDICATE_FLAG = "'"; + + public static final String ZERO_OR_MORE_REGEX = "*"; + + public static final String OR_OPERATOR = "OR"; + + public static final String AND_OPERATOR = "AND"; + + public static final String LEFT_PARANTHESIS = "("; + + public static final String RIGHT_PARANTHESIS = ")"; + + public static SearchOperation getSimpleOperation(final char input) { + switch (input) { + case ':': + return EQUALITY; + case '!': + return NEGATION; + case '>': + return GREATER_THAN; + case '<': + return LESS_THAN; + case '~': + return LIKE; + default: + return null; + } + } +} diff --git a/spring-security-rest-full/src/main/java/org/baeldung/web/util/SpecSearchCriteria.java b/spring-security-rest-full/src/main/java/org/baeldung/web/util/SpecSearchCriteria.java index 4a04d395fa86..3435ff3342b7 100644 --- a/spring-security-rest-full/src/main/java/org/baeldung/web/util/SpecSearchCriteria.java +++ b/spring-security-rest-full/src/main/java/org/baeldung/web/util/SpecSearchCriteria.java @@ -1,44 +1,82 @@ -package org.baeldung.web.util; - -public class SpecSearchCriteria { - - private String key; - private SearchOperation operation; - private Object value; - - public SpecSearchCriteria() { - - } - - public SpecSearchCriteria(final String key, final SearchOperation operation, final Object value) { - super(); - this.key = key; - this.operation = operation; - this.value = value; - } - - public String getKey() { - return key; - } - - public void setKey(final String key) { - this.key = key; - } - - public SearchOperation getOperation() { - return operation; - } - - public void setOperation(final SearchOperation operation) { - this.operation = operation; - } - - public Object getValue() { - return value; - } - - public void setValue(final Object value) { - this.value = value; - } - -} +package org.baeldung.web.util; + +public class SpecSearchCriteria { + + private String key; + private SearchOperation operation; + private Object value; + private boolean orPredicate; + + public SpecSearchCriteria() { + + } + + public SpecSearchCriteria(final String key, final SearchOperation operation, final Object value) { + super(); + this.key = key; + this.operation = operation; + this.value = value; + } + + public SpecSearchCriteria(final String orPredicate, final String key, final SearchOperation operation, final Object value) { + super(); + this.orPredicate = orPredicate != null && orPredicate.equals(SearchOperation.OR_PREDICATE_FLAG); + this.key = key; + this.operation = operation; + this.value = value; + } + + public SpecSearchCriteria(String key, String operation, String prefix, String value, String suffix) { + SearchOperation op = SearchOperation.getSimpleOperation(operation.charAt(0)); + if (op != null) { + if (op == SearchOperation.EQUALITY) { // the operation may be complex operation + final boolean startWithAsterisk = prefix != null && prefix.contains(SearchOperation.ZERO_OR_MORE_REGEX); + final boolean endWithAsterisk = suffix != null && suffix.contains(SearchOperation.ZERO_OR_MORE_REGEX); + + if (startWithAsterisk && endWithAsterisk) { + op = SearchOperation.CONTAINS; + } else if (startWithAsterisk) { + op = SearchOperation.ENDS_WITH; + } else if (endWithAsterisk) { + op = SearchOperation.STARTS_WITH; + } + } + } + this.key = key; + this.operation = op; + this.value = value; + } + + public String getKey() { + return key; + } + + public void setKey(final String key) { + this.key = key; + } + + public SearchOperation getOperation() { + return operation; + } + + public void setOperation(final SearchOperation operation) { + this.operation = operation; + } + + public Object getValue() { + return value; + } + + public void setValue(final Object value) { + this.value = value; + } + + public boolean isOrPredicate() { + return orPredicate; + } + + public void setOrPredicate(boolean orPredicate) { + this.orPredicate = orPredicate; + } + +} diff --git a/spring-security-rest-full/src/main/resources/logback.xml b/spring-security-rest-full/src/main/resources/logback.xml index 1146dade632e..ec0dc2469ae0 100644 --- a/spring-security-rest-full/src/main/resources/logback.xml +++ b/spring-security-rest-full/src/main/resources/logback.xml @@ -1,20 +1,19 @@ + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - + + - - - - - - - - - + + + + + \ No newline at end of file diff --git a/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/JPASpecificationIntegrationTest.java b/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/JPASpecificationIntegrationTest.java index 8bd4857e85f6..d9ae95c87623 100644 --- a/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/JPASpecificationIntegrationTest.java +++ b/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/JPASpecificationIntegrationTest.java @@ -1,119 +1,180 @@ -package org.baeldung.persistence.query; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.collection.IsIn.isIn; -import static org.hamcrest.core.IsNot.not; - -import java.util.List; - -import org.baeldung.persistence.dao.UserRepository; -import org.baeldung.persistence.dao.UserSpecification; -import org.baeldung.persistence.model.User; -import org.baeldung.spring.PersistenceConfig; -import org.baeldung.web.util.SearchOperation; -import org.baeldung.web.util.SpecSearchCriteria; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.jpa.domain.Specifications; -import org.springframework.test.annotation.Rollback; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.transaction.annotation.Transactional; - -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = { PersistenceConfig.class }) -@Transactional -@Rollback -public class JPASpecificationIntegrationTest { - - @Autowired - private UserRepository repository; - - private User userJohn; - - private User userTom; - - @Before - public void init() { - userJohn = new User(); - userJohn.setFirstName("john"); - userJohn.setLastName("doe"); - userJohn.setEmail("john@doe.com"); - userJohn.setAge(22); - repository.save(userJohn); - - userTom = new User(); - userTom.setFirstName("tom"); - userTom.setLastName("doe"); - userTom.setEmail("tom@doe.com"); - userTom.setAge(26); - repository.save(userTom); - } - - @Test - public void givenFirstAndLastName_whenGettingListOfUsers_thenCorrect() { - final UserSpecification spec = new UserSpecification(new SpecSearchCriteria("firstName", SearchOperation.EQUALITY, "john")); - final UserSpecification spec1 = new UserSpecification(new SpecSearchCriteria("lastName", SearchOperation.EQUALITY, "doe")); - final List results = repository.findAll(Specifications.where(spec).and(spec1)); - - assertThat(userJohn, isIn(results)); - assertThat(userTom, not(isIn(results))); - } - - @Test - public void givenFirstNameInverse_whenGettingListOfUsers_thenCorrect() { - final UserSpecification spec = new UserSpecification(new SpecSearchCriteria("firstName", SearchOperation.NEGATION, "john")); - final List results = repository.findAll(Specifications.where(spec)); - - assertThat(userTom, isIn(results)); - assertThat(userJohn, not(isIn(results))); - } - - @Test - public void givenMinAge_whenGettingListOfUsers_thenCorrect() { - final UserSpecification spec = new UserSpecification(new SpecSearchCriteria("age", SearchOperation.GREATER_THAN, "25")); - final List results = repository.findAll(Specifications.where(spec)); - - assertThat(userTom, isIn(results)); - assertThat(userJohn, not(isIn(results))); - } - - @Test - public void givenFirstNamePrefix_whenGettingListOfUsers_thenCorrect() { - final UserSpecification spec = new UserSpecification(new SpecSearchCriteria("firstName", SearchOperation.STARTS_WITH, "jo")); - final List results = repository.findAll(spec); - - assertThat(userJohn, isIn(results)); - assertThat(userTom, not(isIn(results))); - } - - @Test - public void givenFirstNameSuffix_whenGettingListOfUsers_thenCorrect() { - final UserSpecification spec = new UserSpecification(new SpecSearchCriteria("firstName", SearchOperation.ENDS_WITH, "n")); - final List results = repository.findAll(spec); - - assertThat(userJohn, isIn(results)); - assertThat(userTom, not(isIn(results))); - } - - @Test - public void givenFirstNameSubstring_whenGettingListOfUsers_thenCorrect() { - final UserSpecification spec = new UserSpecification(new SpecSearchCriteria("firstName", SearchOperation.CONTAINS, "oh")); - final List results = repository.findAll(spec); - - assertThat(userJohn, isIn(results)); - assertThat(userTom, not(isIn(results))); - } - - @Test - public void givenAgeRange_whenGettingListOfUsers_thenCorrect() { - final UserSpecification spec = new UserSpecification(new SpecSearchCriteria("age", SearchOperation.GREATER_THAN, "20")); - final UserSpecification spec1 = new UserSpecification(new SpecSearchCriteria("age", SearchOperation.LESS_THAN, "25")); - final List results = repository.findAll(Specifications.where(spec).and(spec1)); - - assertThat(userJohn, isIn(results)); - assertThat(userTom, not(isIn(results))); - } -} +package org.baeldung.persistence.query; + +import org.baeldung.persistence.dao.GenericSpecificationsBuilder; +import org.baeldung.persistence.dao.UserRepository; +import org.baeldung.persistence.dao.UserSpecification; +import org.baeldung.persistence.dao.UserSpecificationsBuilder; +import org.baeldung.persistence.model.User; +import org.baeldung.spring.PersistenceConfig; +import org.baeldung.web.util.CriteriaParser; +import org.baeldung.web.util.SearchOperation; +import org.baeldung.web.util.SpecSearchCriteria; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.jpa.domain.Specifications; +import org.springframework.test.annotation.Rollback; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.function.Function; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.collection.IsCollectionWithSize.hasSize; +import static org.hamcrest.collection.IsIn.isIn; +import static org.hamcrest.core.IsNot.not; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = { PersistenceConfig.class }) +@Transactional +@Rollback +public class JPASpecificationIntegrationTest { + + @Autowired + private UserRepository repository; + + private User userJohn; + + private User userTom; + + private User userPercy; + + @Before + public void init() { + userJohn = new User(); + userJohn.setFirstName("john"); + userJohn.setLastName("doe"); + userJohn.setEmail("john@doe.com"); + userJohn.setAge(22); + repository.save(userJohn); + + userTom = new User(); + userTom.setFirstName("tom"); + userTom.setLastName("doe"); + userTom.setEmail("tom@doe.com"); + userTom.setAge(26); + repository.save(userTom); + + userPercy = new User(); + userPercy.setFirstName("percy"); + userPercy.setLastName("blackney"); + userPercy.setEmail("percy@blackney.com"); + userPercy.setAge(30); + repository.save(userPercy); + } + + @Test + public void givenFirstAndLastName_whenGettingListOfUsers_thenCorrect() { + final UserSpecification spec = new UserSpecification(new SpecSearchCriteria("firstName", SearchOperation.EQUALITY, "john")); + final UserSpecification spec1 = new UserSpecification(new SpecSearchCriteria("lastName", SearchOperation.EQUALITY, "doe")); + final List results = repository.findAll(Specifications + .where(spec) + .and(spec1)); + + assertThat(userJohn, isIn(results)); + assertThat(userTom, not(isIn(results))); + } + + @Test + public void givenFirstOrLastName_whenGettingListOfUsers_thenCorrect() { + UserSpecificationsBuilder builder = new UserSpecificationsBuilder(); + + SpecSearchCriteria spec = new SpecSearchCriteria("firstName", SearchOperation.EQUALITY, "john"); + SpecSearchCriteria spec1 = new SpecSearchCriteria("'","lastName", SearchOperation.EQUALITY, "doe"); + + List results = repository.findAll(builder + .with(spec) + .with(spec1) + .build()); + + assertThat(results, hasSize(2)); + assertThat(userJohn, isIn(results)); + assertThat(userTom, isIn(results)); + } + + @Test + public void givenFirstOrLastNameAndAgeGenericBuilder_whenGettingListOfUsers_thenCorrect() { + GenericSpecificationsBuilder builder = new GenericSpecificationsBuilder<>(); + Function> converter = UserSpecification::new; + + CriteriaParser parser=new CriteriaParser(); + List results = repository.findAll(builder.build(parser.parse("( lastName:doe OR firstName:john ) AND age:22"), converter)); + + assertThat(results, hasSize(1)); + assertThat(userJohn, isIn(results)); + assertThat(userTom, not(isIn(results))); + } + + @Test + public void givenFirstOrLastNameGenericBuilder_whenGettingListOfUsers_thenCorrect() { + GenericSpecificationsBuilder builder = new GenericSpecificationsBuilder<>(); + Function> converter = UserSpecification::new; + + builder.with("firstName", ":", "john", null, null); + builder.with("'", "lastName", ":", "doe", null, null); + + List results = repository.findAll(builder.build(converter)); + + assertThat(results, hasSize(2)); + assertThat(userJohn, isIn(results)); + assertThat(userTom, isIn(results)); + } + + @Test + public void givenFirstNameInverse_whenGettingListOfUsers_thenCorrect() { + final UserSpecification spec = new UserSpecification(new SpecSearchCriteria("firstName", SearchOperation.NEGATION, "john")); + final List results = repository.findAll(Specifications.where(spec)); + + assertThat(userTom, isIn(results)); + assertThat(userJohn, not(isIn(results))); + } + + @Test + public void givenMinAge_whenGettingListOfUsers_thenCorrect() { + final UserSpecification spec = new UserSpecification(new SpecSearchCriteria("age", SearchOperation.GREATER_THAN, "25")); + final List results = repository.findAll(Specifications.where(spec)); + assertThat(userTom, isIn(results)); + assertThat(userJohn, not(isIn(results))); + } + + @Test + public void givenFirstNamePrefix_whenGettingListOfUsers_thenCorrect() { + final UserSpecification spec = new UserSpecification(new SpecSearchCriteria("firstName", SearchOperation.STARTS_WITH, "jo")); + final List results = repository.findAll(spec); + assertThat(userJohn, isIn(results)); + assertThat(userTom, not(isIn(results))); + } + + @Test + public void givenFirstNameSuffix_whenGettingListOfUsers_thenCorrect() { + final UserSpecification spec = new UserSpecification(new SpecSearchCriteria("firstName", SearchOperation.ENDS_WITH, "n")); + final List results = repository.findAll(spec); + assertThat(userJohn, isIn(results)); + assertThat(userTom, not(isIn(results))); + } + + @Test + public void givenFirstNameSubstring_whenGettingListOfUsers_thenCorrect() { + final UserSpecification spec = new UserSpecification(new SpecSearchCriteria("firstName", SearchOperation.CONTAINS, "oh")); + final List results = repository.findAll(spec); + + assertThat(userJohn, isIn(results)); + assertThat(userTom, not(isIn(results))); + } + + @Test + public void givenAgeRange_whenGettingListOfUsers_thenCorrect() { + final UserSpecification spec = new UserSpecification(new SpecSearchCriteria("age", SearchOperation.GREATER_THAN, "20")); + final UserSpecification spec1 = new UserSpecification(new SpecSearchCriteria("age", SearchOperation.LESS_THAN, "25")); + final List results = repository.findAll(Specifications + .where(spec) + .and(spec1)); + + assertThat(userJohn, isIn(results)); + assertThat(userTom, not(isIn(results))); + } +} diff --git a/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/JPASpecificationLiveTest.java b/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/JPASpecificationLiveTest.java index 3b85cfb48756..70787266d806 100644 --- a/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/JPASpecificationLiveTest.java +++ b/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/JPASpecificationLiveTest.java @@ -1,112 +1,153 @@ -package org.baeldung.persistence.query; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import org.baeldung.persistence.model.User; -import org.junit.Before; -import org.junit.Test; -import org.springframework.test.context.ActiveProfiles; - -import com.jayway.restassured.RestAssured; -import com.jayway.restassured.response.Response; -import com.jayway.restassured.specification.RequestSpecification; - -//@RunWith(SpringJUnit4ClassRunner.class) -//@ContextConfiguration(classes = { ConfigTest.class, PersistenceConfig.class }, loader = AnnotationConfigContextLoader.class) -@ActiveProfiles("test") -public class JPASpecificationLiveTest { - - // @Autowired - // private UserRepository repository; - - private User userJohn; - - private User userTom; - - private final String URL_PREFIX = "http://localhost:8082/spring-security-rest-full/auth/users/spec?search="; - - @Before - public void init() { - userJohn = new User(); - userJohn.setFirstName("john"); - userJohn.setLastName("doe"); - userJohn.setEmail("john@doe.com"); - userJohn.setAge(22); - // repository.save(userJohn); - - userTom = new User(); - userTom.setFirstName("tom"); - userTom.setLastName("doe"); - userTom.setEmail("tom@doe.com"); - userTom.setAge(26); - // repository.save(userTom); - } - - @Test - public void givenFirstAndLastName_whenGettingListOfUsers_thenCorrect() { - final Response response = givenAuth().get(URL_PREFIX + "firstName:john,lastName:doe"); - final String result = response.body().asString(); - - assertTrue(result.contains(userJohn.getEmail())); - assertFalse(result.contains(userTom.getEmail())); - } - - @Test - public void givenFirstNameInverse_whenGettingListOfUsers_thenCorrect() { - final Response response = givenAuth().get(URL_PREFIX + "firstName!john"); - final String result = response.body().asString(); - - assertTrue(result.contains(userTom.getEmail())); - assertFalse(result.contains(userJohn.getEmail())); - } - - @Test - public void givenMinAge_whenGettingListOfUsers_thenCorrect() { - final Response response = givenAuth().get(URL_PREFIX + "age>25"); - final String result = response.body().asString(); - - assertTrue(result.contains(userTom.getEmail())); - assertFalse(result.contains(userJohn.getEmail())); - } - - @Test - public void givenFirstNamePrefix_whenGettingListOfUsers_thenCorrect() { - final Response response = givenAuth().get(URL_PREFIX + "firstName:jo*"); - final String result = response.body().asString(); - - assertTrue(result.contains(userJohn.getEmail())); - assertFalse(result.contains(userTom.getEmail())); - } - - @Test - public void givenFirstNameSuffix_whenGettingListOfUsers_thenCorrect() { - final Response response = givenAuth().get(URL_PREFIX + "firstName:*n"); - final String result = response.body().asString(); - - assertTrue(result.contains(userJohn.getEmail())); - assertFalse(result.contains(userTom.getEmail())); - } - - @Test - public void givenFirstNameSubstring_whenGettingListOfUsers_thenCorrect() { - final Response response = givenAuth().get(URL_PREFIX + "firstName:*oh*"); - final String result = response.body().asString(); - - assertTrue(result.contains(userJohn.getEmail())); - assertFalse(result.contains(userTom.getEmail())); - } - - @Test - public void givenAgeRange_whenGettingListOfUsers_thenCorrect() { - final Response response = givenAuth().get(URL_PREFIX + "age>20,age<25"); - final String result = response.body().asString(); - - assertTrue(result.contains(userJohn.getEmail())); - assertFalse(result.contains(userTom.getEmail())); - } - - private final RequestSpecification givenAuth() { - return RestAssured.given().auth().preemptive().basic("user1", "user1Pass"); - } -} +package org.baeldung.persistence.query; + +import com.jayway.restassured.RestAssured; +import com.jayway.restassured.response.Response; +import com.jayway.restassured.specification.RequestSpecification; +import org.baeldung.persistence.model.User; +import org.junit.Before; +import org.junit.Test; +import org.springframework.test.context.ActiveProfiles; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +//@RunWith(SpringJUnit4ClassRunner.class) +//@ContextConfiguration(classes = { ConfigTest.class, +// PersistenceConfig.class }, loader = AnnotationConfigContextLoader.class) +@ActiveProfiles("test") +public class JPASpecificationLiveTest { + + // @Autowired + // private UserRepository repository; + + private User userJohn; + + private User userTom; + + private final String URL_PREFIX = "http://localhost:8082/spring-security-rest-full/auth/users/spec?search="; + + @Before + public void init() { + userJohn = new User(); + userJohn.setFirstName("john"); + userJohn.setLastName("doe"); + userJohn.setEmail("john@doe.com"); + userJohn.setAge(22); + // repository.save(userJohn); + + userTom = new User(); + userTom.setFirstName("tom"); + userTom.setLastName("doe"); + userTom.setEmail("tom@doe.com"); + userTom.setAge(26); + // repository.save(userTom); + } + + private final String EURL_PREFIX = "http://localhost:8082/spring-security-rest-full/auth/users/espec?search="; + + @Test + public void givenFirstOrLastName_whenGettingListOfUsers_thenCorrect() { + final Response response = givenAuth().get(EURL_PREFIX + "firstName:john,'lastName:doe"); + final String result = response.body() + .asString(); + assertTrue(result.contains(userJohn.getEmail())); + assertTrue(result.contains(userTom.getEmail())); + } + + @Test + public void givenFirstAndLastName_whenGettingListOfUsers_thenCorrect() { + final Response response = givenAuth().get(URL_PREFIX + "firstName:john,lastName:doe"); + final String result = response.body() + .asString(); + + assertTrue(result.contains(userJohn.getEmail())); + assertFalse(result.contains(userTom.getEmail())); + } + + @Test + public void givenFirstNameInverse_whenGettingListOfUsers_thenCorrect() { + final Response response = givenAuth().get(URL_PREFIX + "firstName!john"); + final String result = response.body() + .asString(); + + assertTrue(result.contains(userTom.getEmail())); + assertFalse(result.contains(userJohn.getEmail())); + } + + @Test + public void givenMinAge_whenGettingListOfUsers_thenCorrect() { + final Response response = givenAuth().get(URL_PREFIX + "age>25"); + final String result = response.body() + .asString(); + + assertTrue(result.contains(userTom.getEmail())); + assertFalse(result.contains(userJohn.getEmail())); + } + + @Test + public void givenFirstNamePrefix_whenGettingListOfUsers_thenCorrect() { + final Response response = givenAuth().get(URL_PREFIX + "firstName:jo*"); + final String result = response.body() + .asString(); + + assertTrue(result.contains(userJohn.getEmail())); + assertFalse(result.contains(userTom.getEmail())); + } + + @Test + public void givenFirstNameSuffix_whenGettingListOfUsers_thenCorrect() { + final Response response = givenAuth().get(URL_PREFIX + "firstName:*n"); + final String result = response.body() + .asString(); + + assertTrue(result.contains(userJohn.getEmail())); + assertFalse(result.contains(userTom.getEmail())); + } + + @Test + public void givenFirstNameSubstring_whenGettingListOfUsers_thenCorrect() { + final Response response = givenAuth().get(URL_PREFIX + "firstName:*oh*"); + final String result = response.body() + .asString(); + + assertTrue(result.contains(userJohn.getEmail())); + assertFalse(result.contains(userTom.getEmail())); + } + + @Test + public void givenAgeRange_whenGettingListOfUsers_thenCorrect() { + final Response response = givenAuth().get(URL_PREFIX + "age>20,age<25"); + final String result = response.body() + .asString(); + + assertTrue(result.contains(userJohn.getEmail())); + assertFalse(result.contains(userTom.getEmail())); + } + + private final String ADV_URL_PREFIX = "http://localhost:8082/spring-security-rest-full/auth/users/spec/adv?search="; + + @Test + public void givenFirstOrLastName_whenGettingAdvListOfUsers_thenCorrect() { + final Response response = givenAuth().get(ADV_URL_PREFIX + "firstName:john OR lastName:doe"); + final String result = response.body() + .asString(); + assertTrue(result.contains(userJohn.getEmail())); + assertTrue(result.contains(userTom.getEmail())); + } + + @Test + public void givenFirstOrFirstNameAndAge_whenGettingAdvListOfUsers_thenCorrect() { + final Response response = givenAuth().get(ADV_URL_PREFIX + "( firstName:john OR firstName:tom ) AND age>22"); + final String result = response.body() + .asString(); + assertFalse(result.contains(userJohn.getEmail())); + assertTrue(result.contains(userTom.getEmail())); + } + + private final RequestSpecification givenAuth() { + return RestAssured.given() + .auth() + .preemptive() + .basic("user1", "user1Pass"); + } +} diff --git a/spring-security-rest/pom.xml b/spring-security-rest/pom.xml index 320e84fa7adc..6052f7d42ecd 100644 --- a/spring-security-rest/pom.xml +++ b/spring-security-rest/pom.xml @@ -145,11 +145,6 @@ ${org.slf4j.version} - - org.slf4j - log4j-over-slf4j - ${org.slf4j.version} - @@ -259,10 +254,10 @@ org.apache.maven.plugins maven-surefire-plugin - ${maven-surefire-plugin.version} **/*LiveTest.java + **/*IntegrationTest.java diff --git a/spring-security-rest/src/main/java/org/baeldung/web/controller/AsyncController.java b/spring-security-rest/src/main/java/org/baeldung/web/controller/AsyncController.java index 9a69528efed0..456eeaaeac8d 100644 --- a/spring-security-rest/src/main/java/org/baeldung/web/controller/AsyncController.java +++ b/spring-security-rest/src/main/java/org/baeldung/web/controller/AsyncController.java @@ -2,7 +2,9 @@ import java.util.concurrent.Callable; -import org.apache.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import org.baeldung.web.service.AsyncService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.context.SecurityContextHolder; @@ -14,7 +16,7 @@ @Controller public class AsyncController { - private static final Logger log = Logger.getLogger(AsyncService.class); + private static final Logger log = LoggerFactory.getLogger(AsyncService.class); @Autowired private AsyncService asyncService; diff --git a/spring-security-rest/src/main/java/org/baeldung/web/service/AsyncServiceImpl.java b/spring-security-rest/src/main/java/org/baeldung/web/service/AsyncServiceImpl.java index fb954a49a7f0..caaaa8e0dcc5 100644 --- a/spring-security-rest/src/main/java/org/baeldung/web/service/AsyncServiceImpl.java +++ b/spring-security-rest/src/main/java/org/baeldung/web/service/AsyncServiceImpl.java @@ -2,7 +2,9 @@ import java.util.concurrent.Callable; -import org.apache.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import org.springframework.scheduling.annotation.Async; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; @@ -10,7 +12,7 @@ @Service public class AsyncServiceImpl implements AsyncService { - private static final Logger log = Logger.getLogger(AsyncService.class); + private static final Logger log = LoggerFactory.getLogger(AsyncService.class); @Async @Override diff --git a/spring-security-rest/src/main/resources/logback.xml b/spring-security-rest/src/main/resources/logback.xml index 1146dade632e..ec0dc2469ae0 100644 --- a/spring-security-rest/src/main/resources/logback.xml +++ b/spring-security-rest/src/main/resources/logback.xml @@ -1,20 +1,19 @@ + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - + + - - - - - - - - - + + + + + \ No newline at end of file diff --git a/spring-security-rest/src/test/java/org/baeldung/web/AsyncControllerTest.java b/spring-security-rest/src/test/java/org/baeldung/web/AsyncControllerIntegrationTest.java similarity index 97% rename from spring-security-rest/src/test/java/org/baeldung/web/AsyncControllerTest.java rename to spring-security-rest/src/test/java/org/baeldung/web/AsyncControllerIntegrationTest.java index 9cd92880a7b3..a09c225a4a86 100644 --- a/spring-security-rest/src/test/java/org/baeldung/web/AsyncControllerTest.java +++ b/spring-security-rest/src/test/java/org/baeldung/web/AsyncControllerIntegrationTest.java @@ -1,7 +1,5 @@ package org.baeldung.web; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - import org.baeldung.spring.ClientWebConfig; import org.baeldung.spring.SecurityJavaConfig; import org.baeldung.spring.WebConfig; @@ -20,10 +18,12 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @ContextConfiguration(classes = { ClientWebConfig.class, SecurityJavaConfig.class, WebConfig.class }) -public class AsyncControllerTest { +public class AsyncControllerIntegrationTest { @Autowired WebApplicationContext wac; diff --git a/spring-security-stormpath/README.md b/spring-security-stormpath/README.md new file mode 100644 index 000000000000..f83882112f59 --- /dev/null +++ b/spring-security-stormpath/README.md @@ -0,0 +1,3 @@ +### Relevant articles + +- [Spring Security with Stormpath](http://www.baeldung.com/spring-security-stormpath) diff --git a/spring-spel/pom.xml b/spring-spel/pom.xml index c3817ce2bda7..5bb848071526 100644 --- a/spring-spel/pom.xml +++ b/spring-spel/pom.xml @@ -31,6 +31,12 @@ org.springframework spring-context ${springframework.version} + + + commons-logging + commons-logging + + org.springframework diff --git a/spring-state-machine/README.md b/spring-state-machine/README.md new file mode 100644 index 000000000000..a2668fb650d0 --- /dev/null +++ b/spring-state-machine/README.md @@ -0,0 +1,3 @@ +### Relevant articles + +- [A Guide to the Spring State Machine Project](http://www.baeldung.com/spring-state-machine) diff --git a/spring-state-machine/bpmn/forkjoin.bpmn b/spring-state-machine/bpmn/forkjoin.bpmn new file mode 100644 index 000000000000..0cb060f74bb5 --- /dev/null +++ b/spring-state-machine/bpmn/forkjoin.bpmn @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-state-machine/bpmn/img/forkjoin.png b/spring-state-machine/bpmn/img/forkjoin.png new file mode 100644 index 000000000000..642ab6949d18 Binary files /dev/null and b/spring-state-machine/bpmn/img/forkjoin.png differ diff --git a/spring-state-machine/bpmn/img/simple.png b/spring-state-machine/bpmn/img/simple.png new file mode 100644 index 000000000000..7a79bf1d895a Binary files /dev/null and b/spring-state-machine/bpmn/img/simple.png differ diff --git a/spring-state-machine/bpmn/simple.bpmn b/spring-state-machine/bpmn/simple.bpmn new file mode 100644 index 000000000000..8ed463e9f9c9 --- /dev/null +++ b/spring-state-machine/bpmn/simple.bpmn @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-state-machine/pom.xml b/spring-state-machine/pom.xml new file mode 100644 index 000000000000..9dcb51fe4e92 --- /dev/null +++ b/spring-state-machine/pom.xml @@ -0,0 +1,63 @@ + + + + parent-modules + com.baeldung + 1.0.0-SNAPSHOT + + 4.0.0 + + spring-state-machine + + 1.8 + 1.8 + + + + + org.springframework.statemachine + spring-statemachine-core + 1.2.3.RELEASE + + + org.springframework + spring-test + 4.3.7.RELEASE + + + junit + junit + 4.12 + test + + + + com.jayway.awaitility + awaitility + 1.7.0 + test + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + **/*IntegrationTest.java + **/*LiveTest.java + + + + + + + + + \ No newline at end of file diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewEvents.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewEvents.java new file mode 100644 index 000000000000..300bd6027ea1 --- /dev/null +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewEvents.java @@ -0,0 +1,5 @@ +package com.baeldung.spring.statemachine.applicationreview; + +public enum ApplicationReviewEvents { + APPROVE, REJECT +} diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewStates.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewStates.java new file mode 100644 index 000000000000..3d173e74716d --- /dev/null +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewStates.java @@ -0,0 +1,5 @@ +package com.baeldung.spring.statemachine.applicationreview; + +public enum ApplicationReviewStates { + PEER_REVIEW, PRINCIPAL_REVIEW, APPROVED, REJECTED +} diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/ForkJoinStateMachineConfiguration.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/ForkJoinStateMachineConfiguration.java new file mode 100644 index 000000000000..3a3e632c5124 --- /dev/null +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/ForkJoinStateMachineConfiguration.java @@ -0,0 +1,74 @@ +package com.baeldung.spring.statemachine.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.statemachine.config.EnableStateMachine; +import org.springframework.statemachine.config.StateMachineConfigurerAdapter; +import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer; +import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; +import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; +import org.springframework.statemachine.guard.Guard; + +@Configuration +@EnableStateMachine +public class ForkJoinStateMachineConfiguration extends StateMachineConfigurerAdapter { + + @Override + public void configure(StateMachineConfigurationConfigurer config) + throws Exception { + config + .withConfiguration() + .autoStartup(true) + .listener(new StateMachineListener()); + } + + @Override + public void configure(StateMachineStateConfigurer states) throws Exception { + states + .withStates() + .initial("SI") + .fork("SFork") + .join("SJoin") + .end("SF") + .and() + .withStates() + .parent("SFork") + .initial("Sub1-1") + .end("Sub1-2") + .and() + .withStates() + .parent("SFork") + .initial("Sub2-1") + .end("Sub2-2"); + } + + @Override + public void configure(StateMachineTransitionConfigurer transitions) throws Exception { + transitions.withExternal() + .source("SI").target("SFork").event("E1") + .and().withExternal() + .source("Sub1-1").target("Sub1-2").event("sub1") + .and().withExternal() + .source("Sub2-1").target("Sub2-2").event("sub2") + .and() + .withFork() + .source("SFork") + .target("Sub1-1") + .target("Sub2-1") + .and() + .withJoin() + .source("Sub1-2") + .source("Sub2-2") + .target("SJoin"); + } + + @Bean + public Guard mediumGuard() { + return (ctx) -> false; + } + + @Bean + public Guard highGuard() { + return (ctx) -> false; + } +} \ No newline at end of file diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/HierarchicalStateMachineConfiguration.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/HierarchicalStateMachineConfiguration.java new file mode 100644 index 000000000000..2bf9405c39ef --- /dev/null +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/HierarchicalStateMachineConfiguration.java @@ -0,0 +1,47 @@ +package com.baeldung.spring.statemachine.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.statemachine.config.EnableStateMachine; +import org.springframework.statemachine.config.StateMachineConfigurerAdapter; +import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer; +import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; +import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; + +@Configuration +@EnableStateMachine +public class HierarchicalStateMachineConfiguration extends StateMachineConfigurerAdapter { + + @Override + public void configure(StateMachineConfigurationConfigurer config) + throws Exception { + config + .withConfiguration() + .autoStartup(true) + .listener(new StateMachineListener()); + } + + @Override + public void configure(StateMachineStateConfigurer states) throws Exception { + states + .withStates() + .initial("SI") + .state("SI") + .end("SF") + .and() + .withStates() + .parent("SI") + .initial("SUB1") + .state("SUB2") + .end("SUBEND"); + } + + @Override + public void configure(StateMachineTransitionConfigurer transitions) throws Exception { + transitions.withExternal() + .source("SI").target("SF").event("end") + .and().withExternal() + .source("SUB1").target("SUB2").event("se1") + .and().withExternal() + .source("SUB2").target("SUBEND").event("s-end"); + } +} \ No newline at end of file diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/JunctionStateMachineConfiguration.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/JunctionStateMachineConfiguration.java new file mode 100644 index 000000000000..2f48a9dbb5ae --- /dev/null +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/JunctionStateMachineConfiguration.java @@ -0,0 +1,60 @@ +package com.baeldung.spring.statemachine.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.statemachine.config.EnableStateMachine; +import org.springframework.statemachine.config.StateMachineConfigurerAdapter; +import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer; +import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; +import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; +import org.springframework.statemachine.guard.Guard; + +@Configuration +@EnableStateMachine +public class JunctionStateMachineConfiguration extends StateMachineConfigurerAdapter { + + @Override + public void configure(StateMachineConfigurationConfigurer config) + throws Exception { + config + .withConfiguration() + .autoStartup(true) + .listener(new StateMachineListener()); + } + + @Override + public void configure(StateMachineStateConfigurer states) throws Exception { + states + .withStates() + .initial("SI") + .junction("SJ") + .state("high") + .state("medium") + .state("low") + .end("SF"); + } + + @Override + public void configure(StateMachineTransitionConfigurer transitions) throws Exception { + transitions.withExternal() + .source("SI").target("SJ").event("E1") + .and() + .withJunction() + .source("SJ") + .first("high", highGuard()) + .then("medium", mediumGuard()) + .last("low") + .and().withExternal() + .source("low").target("SF").event("end"); + } + + @Bean + public Guard mediumGuard() { + return (ctx) -> false; + } + + @Bean + public Guard highGuard() { + return (ctx) -> false; + } +} \ No newline at end of file diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleEnumStateMachineConfiguration.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleEnumStateMachineConfiguration.java new file mode 100644 index 000000000000..5339dea7d0f6 --- /dev/null +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleEnumStateMachineConfiguration.java @@ -0,0 +1,47 @@ +package com.baeldung.spring.statemachine.config; + +import com.baeldung.spring.statemachine.applicationreview.ApplicationReviewEvents; +import com.baeldung.spring.statemachine.applicationreview.ApplicationReviewStates; +import org.springframework.context.annotation.Configuration; +import org.springframework.statemachine.config.EnableStateMachine; +import org.springframework.statemachine.config.StateMachineConfigurerAdapter; +import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer; +import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; +import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; + +@Configuration +@EnableStateMachine +public class SimpleEnumStateMachineConfiguration extends StateMachineConfigurerAdapter { + + @Override + public void configure(StateMachineConfigurationConfigurer config) + throws Exception { + config + .withConfiguration() + .autoStartup(true) + .listener(new StateMachineListener()); + } + + @Override + public void configure(StateMachineStateConfigurer states) throws Exception { + states + .withStates() + .initial(ApplicationReviewStates.PEER_REVIEW) + .state(ApplicationReviewStates.PRINCIPAL_REVIEW) + .end(ApplicationReviewStates.APPROVED) + .end(ApplicationReviewStates.REJECTED); + + } + + @Override + public void configure(StateMachineTransitionConfigurer transitions) throws Exception { + transitions.withExternal() + .source(ApplicationReviewStates.PEER_REVIEW).target(ApplicationReviewStates.PRINCIPAL_REVIEW).event(ApplicationReviewEvents.APPROVE) + .and().withExternal() + .source(ApplicationReviewStates.PRINCIPAL_REVIEW).target(ApplicationReviewStates.APPROVED).event(ApplicationReviewEvents.APPROVE) + .and().withExternal() + .source(ApplicationReviewStates.PEER_REVIEW).target(ApplicationReviewStates.REJECTED).event(ApplicationReviewEvents.REJECT) + .and().withExternal() + .source(ApplicationReviewStates.PRINCIPAL_REVIEW).target(ApplicationReviewStates.REJECTED).event(ApplicationReviewEvents.REJECT); + } +} \ No newline at end of file diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleStateMachineConfiguration.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleStateMachineConfiguration.java new file mode 100644 index 000000000000..d1b1ce001cb3 --- /dev/null +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleStateMachineConfiguration.java @@ -0,0 +1,146 @@ +package com.baeldung.spring.statemachine.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.statemachine.action.Action; +import org.springframework.statemachine.config.EnableStateMachine; +import org.springframework.statemachine.config.StateMachineConfigurerAdapter; +import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer; +import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; +import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; +import org.springframework.statemachine.guard.Guard; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.logging.Logger; + +@Configuration +@EnableStateMachine +public class SimpleStateMachineConfiguration extends StateMachineConfigurerAdapter { + + public static final Logger LOGGER = Logger.getLogger(SimpleStateMachineConfiguration.class.getName()); + + @Override + public void configure(StateMachineConfigurationConfigurer config) throws Exception { + config + .withConfiguration() + .autoStartup(true) + .listener(new StateMachineListener()); + } + + @Override + public void configure(StateMachineStateConfigurer states) throws Exception { + states + .withStates() + .initial("SI") + .end("SF") + .states(new HashSet<>(Arrays.asList("S1", "S2"))) + .stateEntry("S3", entryAction()) + .stateExit("S3", exitAction()) + .state("S4", executeAction(), errorAction()) + .stateDo("S5", executeAction()); + + } + + @Override + public void configure(StateMachineTransitionConfigurer transitions) throws Exception { + transitions + .withExternal() + .source("SI") + .target("S1") + .event("E1") + .action(initAction()) + .and() + .withExternal() + .source("S1") + .target("S2") + .event("E2") + .and() + .withExternal() + .source("SI") + .target("S3") + .event("E3") + .and() + .withExternal() + .source("S3") + .target("S4") + .event("E4") + .and() + .withExternal() + .source("S4") + .target("S5") + .event("E5") + .and() + .withExternal() + .source("S5") + .target("SF") + .event("end") + .guard(simpleGuard()); + } + + @Bean + public Guard simpleGuard() { + return (ctx) -> { + int approvalCount = (int) ctx + .getExtendedState() + .getVariables() + .getOrDefault("approvalCount", 0); + return approvalCount > 0; + }; + } + + @Bean + public Action entryAction() { + return ctx -> LOGGER.info("Entry " + ctx + .getTarget() + .getId()); + } + + @Bean + public Action doAction() { + return ctx -> LOGGER.info("Do " + ctx + .getTarget() + .getId()); + } + + @Bean + public Action executeAction() { + return ctx -> { + LOGGER.info("Execute " + ctx + .getTarget() + .getId()); + int approvals = (int) ctx + .getExtendedState() + .getVariables() + .getOrDefault("approvalCount", 0); + approvals++; + ctx + .getExtendedState() + .getVariables() + .put("approvalCount", approvals); + }; + } + + @Bean + public Action exitAction() { + return ctx -> LOGGER.info("Exit " + ctx + .getSource() + .getId() + " -> " + ctx + .getTarget() + .getId()); + } + + @Bean + public Action errorAction() { + return ctx -> LOGGER.info("Error " + ctx + .getSource() + .getId() + ctx.getException()); + } + + @Bean + public Action initAction() { + return ctx -> LOGGER.info(ctx + .getTarget() + .getId()); + } +} \ No newline at end of file diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/StateMachineListener.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/StateMachineListener.java new file mode 100644 index 000000000000..47a274404e49 --- /dev/null +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/StateMachineListener.java @@ -0,0 +1,16 @@ +package com.baeldung.spring.statemachine.config; + +import org.springframework.statemachine.listener.StateMachineListenerAdapter; +import org.springframework.statemachine.state.State; + +import java.util.logging.Logger; + +public class StateMachineListener extends StateMachineListenerAdapter { + + public static final Logger LOGGER = Logger.getLogger(StateMachineListener.class.getName()); + + @Override + public void stateChanged(State from, State to) { + LOGGER.info(String.format("Transitioned from %s to %s%n", from == null ? "none" : from.getId(), to.getId())); + } +} diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/ForkJoinStateMachineIntegrationTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/ForkJoinStateMachineIntegrationTest.java new file mode 100644 index 000000000000..b34d5c47c6ef --- /dev/null +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/ForkJoinStateMachineIntegrationTest.java @@ -0,0 +1,60 @@ +package com.baeldung.spring.statemachine; + +import com.baeldung.spring.statemachine.config.ForkJoinStateMachineConfiguration; +import com.baeldung.spring.statemachine.config.JunctionStateMachineConfiguration; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.statemachine.StateMachine; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import javax.annotation.Resource; +import java.util.Arrays; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = ForkJoinStateMachineConfiguration.class) +public class ForkJoinStateMachineIntegrationTest { + + @Autowired + private StateMachine stateMachine; + + @Before + public void setUp() { + stateMachine.start(); + } + + @Test + public void whenForkStateEntered_thenMultipleSubStatesEntered() { + boolean success = stateMachine.sendEvent("E1"); + + assertTrue(success); + + assertTrue(Arrays.asList("SFork", "Sub1-1", "Sub2-1").containsAll(stateMachine.getState().getIds())); + } + + @Test + public void whenAllConfiguredJoinEntryStatesAreEntered_thenTransitionToJoinState() { + + boolean success = stateMachine.sendEvent("E1"); + + assertTrue(success); + + assertTrue(Arrays.asList("SFork", "Sub1-1", "Sub2-1").containsAll(stateMachine.getState().getIds())); + + assertTrue(stateMachine.sendEvent("sub1")); + assertTrue(stateMachine.sendEvent("sub2")); + assertEquals("SJoin", stateMachine.getState().getId()); + } + + @After + public void tearDown() { + stateMachine.stop(); + } +} diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/HierarchicalStateMachineIntegrationTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/HierarchicalStateMachineIntegrationTest.java new file mode 100644 index 000000000000..3d7c0be82861 --- /dev/null +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/HierarchicalStateMachineIntegrationTest.java @@ -0,0 +1,55 @@ +package com.baeldung.spring.statemachine; + +import com.baeldung.spring.statemachine.config.HierarchicalStateMachineConfiguration; +import com.baeldung.spring.statemachine.config.JunctionStateMachineConfiguration; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.statemachine.StateMachine; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import javax.annotation.Resource; +import java.util.Arrays; + +import static org.junit.Assert.assertEquals; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = HierarchicalStateMachineConfiguration.class) +public class HierarchicalStateMachineIntegrationTest { + + @Autowired + private StateMachine stateMachine; + + @Before + public void setUp() { + stateMachine.start(); + } + + @Test + public void whenTransitionToSubMachine_thenSubStateIsEntered() { + + assertEquals(Arrays.asList("SI", "SUB1"), stateMachine.getState().getIds()); + + stateMachine.sendEvent("se1"); + + assertEquals(Arrays.asList("SI", "SUB2"), stateMachine.getState().getIds()); + + stateMachine.sendEvent("s-end"); + + assertEquals(Arrays.asList("SI", "SUBEND"), stateMachine.getState().getIds()); + + stateMachine.sendEvent("end"); + + assertEquals(1, stateMachine.getState().getIds().size()); + assertEquals("SF", stateMachine.getState().getId()); + } + + @After + public void tearDown() { + stateMachine.stop(); + } +} diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/JunctionStateMachineIntegrationTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/JunctionStateMachineIntegrationTest.java new file mode 100644 index 000000000000..93de23fad3a6 --- /dev/null +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/JunctionStateMachineIntegrationTest.java @@ -0,0 +1,44 @@ +package com.baeldung.spring.statemachine; + +import com.baeldung.spring.statemachine.config.JunctionStateMachineConfiguration; +import com.baeldung.spring.statemachine.config.SimpleEnumStateMachineConfiguration; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.statemachine.StateMachine; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import javax.annotation.Resource; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = JunctionStateMachineConfiguration.class) +public class JunctionStateMachineIntegrationTest { + + @Autowired + private StateMachine stateMachine; + + @Before + public void setUp() { + stateMachine.start(); + } + + @Test + public void whenTransitioningToJunction_thenArriveAtSubJunctionNode() { + + stateMachine.sendEvent("E1"); + Assert.assertEquals("low", stateMachine.getState().getId()); + + stateMachine.sendEvent("end"); + Assert.assertEquals("SF", stateMachine.getState().getId()); + } + + @After + public void tearDown() { + stateMachine.stop(); + } +} diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateEnumMachineIntegrationTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateEnumMachineIntegrationTest.java new file mode 100644 index 000000000000..9074ece0018c --- /dev/null +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateEnumMachineIntegrationTest.java @@ -0,0 +1,46 @@ +package com.baeldung.spring.statemachine; + +import com.baeldung.spring.statemachine.applicationreview.ApplicationReviewEvents; +import com.baeldung.spring.statemachine.applicationreview.ApplicationReviewStates; +import com.baeldung.spring.statemachine.config.SimpleEnumStateMachineConfiguration; +import com.baeldung.spring.statemachine.config.SimpleStateMachineConfiguration; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.statemachine.StateMachine; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import javax.annotation.Resource; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = SimpleEnumStateMachineConfiguration.class) +public class StateEnumMachineIntegrationTest { + + @Autowired + private StateMachine stateMachine; + + @Before + public void setUp() { + stateMachine.start(); + } + + @Test + public void whenStateMachineConfiguredWithEnums_thenStateMachineAcceptsEnumEvents() { + assertTrue(stateMachine.sendEvent(ApplicationReviewEvents.APPROVE)); + assertEquals(ApplicationReviewStates.PRINCIPAL_REVIEW, stateMachine.getState().getId()); + assertTrue(stateMachine.sendEvent(ApplicationReviewEvents.REJECT)); + assertEquals(ApplicationReviewStates.REJECTED, stateMachine.getState().getId()); + } + + @After + public void tearDown() { + stateMachine.stop(); + } +} diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineBuilderTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineBuilderTest.java new file mode 100644 index 000000000000..a5e4d07f1802 --- /dev/null +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineBuilderTest.java @@ -0,0 +1,35 @@ +package com.baeldung.spring.statemachine; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.springframework.statemachine.StateMachine; +import org.springframework.statemachine.config.StateMachineBuilder; + +public class StateMachineBuilderTest { + + @Test + public void whenUseStateMachineBuilder_thenBuildSuccessAndMachineWorks() throws Exception { + StateMachineBuilder.Builder builder = StateMachineBuilder.builder(); + builder.configureStates().withStates() + .initial("SI") + .state("S1") + .end("SF"); + + builder.configureTransitions() + .withExternal() + .source("SI").target("S1").event("E1") + .and().withExternal() + .source("S1").target("SF").event("E2"); + + StateMachine machine = builder.build(); + + machine.start(); + + machine.sendEvent("E1"); + assertEquals("S1", machine.getState().getId()); + + machine.sendEvent("E2"); + assertEquals("SF", machine.getState().getId()); + } +} diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineIntegrationTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineIntegrationTest.java new file mode 100644 index 000000000000..25df7c8cd352 --- /dev/null +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineIntegrationTest.java @@ -0,0 +1,68 @@ +package com.baeldung.spring.statemachine; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.statemachine.StateMachine; + +import com.baeldung.spring.statemachine.config.SimpleStateMachineConfiguration; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.junit4.SpringRunner; + +import javax.annotation.Resource; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = SimpleStateMachineConfiguration.class) +public class StateMachineIntegrationTest { + + @Autowired + private StateMachine stateMachine; + + @Before + public void setUp() { + stateMachine.start(); + } + + @Test + public void whenSimpleStringStateMachineEvents_thenEndState() { + assertEquals("SI", stateMachine.getState().getId()); + + stateMachine.sendEvent("E1"); + assertEquals("S1", stateMachine.getState().getId()); + + stateMachine.sendEvent("E2"); + assertEquals("S2", stateMachine.getState().getId()); + } + + @Test + public void whenSimpleStringMachineActionState_thenActionExecuted() throws InterruptedException { + + stateMachine.sendEvent("E3"); + assertEquals("S3", stateMachine.getState().getId()); + + boolean acceptedE4 = stateMachine.sendEvent("E4"); + + assertTrue(acceptedE4); + assertEquals("S4", stateMachine.getState().getId()); + + stateMachine.sendEvent("E5"); + assertEquals("S5", stateMachine.getState().getId()); + + stateMachine.sendEvent("end"); + assertEquals("SF", stateMachine.getState().getId()); + + assertEquals(2, stateMachine.getExtendedState().getVariables().get("approvalCount")); + } + + @After + public void tearDown() { + stateMachine.stop(); + } +} diff --git a/spring-thymeleaf/pom.xml b/spring-thymeleaf/pom.xml index f5dfc9931e08..7082441ceb4d 100644 --- a/spring-thymeleaf/pom.xml +++ b/spring-thymeleaf/pom.xml @@ -102,11 +102,6 @@ ${org.slf4j.version} - - org.slf4j - log4j-over-slf4j - ${org.slf4j.version} - javax.servlet @@ -176,6 +171,8 @@ maven-surefire-plugin ${maven-surefire-plugin.version} + 3 + true **/*IntegrationTest.java **/*LiveTest.java diff --git a/spring-thymeleaf/src/main/resources/logback.xml b/spring-thymeleaf/src/main/resources/logback.xml index 1146dade632e..ec0dc2469ae0 100644 --- a/spring-thymeleaf/src/main/resources/logback.xml +++ b/spring-thymeleaf/src/main/resources/logback.xml @@ -1,20 +1,19 @@ + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - + + - - - - - - - - - + + + + + \ No newline at end of file diff --git a/spring-userservice/pom.xml b/spring-userservice/pom.xml index c0099173eeb8..58a7cf748f8d 100644 --- a/spring-userservice/pom.xml +++ b/spring-userservice/pom.xml @@ -127,6 +127,12 @@ org.springframework spring-core ${org.springframework.version} + + + commons-logging + commons-logging + + org.springframework @@ -239,6 +245,9 @@ maven-surefire-plugin ${maven-surefire-plugin.version} + 3 + true + **/*IntegrationTest.java diff --git a/spring-zuul/pom.xml b/spring-zuul/pom.xml index 50b20b879154..02fe589a3ac7 100644 --- a/spring-zuul/pom.xml +++ b/spring-zuul/pom.xml @@ -45,7 +45,6 @@ org.apache.maven.plugins maven-surefire-plugin - ${maven-surefire-plugin.version} true diff --git a/stripe/.gitignore b/stripe/.gitignore new file mode 100644 index 000000000000..be941a016b3a --- /dev/null +++ b/stripe/.gitignore @@ -0,0 +1,27 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +nbproject/private/ +build/ +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ + +### +desktop.ini \ No newline at end of file diff --git a/stripe/pom.xml b/stripe/pom.xml new file mode 100644 index 000000000000..5bb3d4207a5b --- /dev/null +++ b/stripe/pom.xml @@ -0,0 +1,66 @@ + + + 4.0.0 + + com.baeldung.stripe + stripe + 0.0.1-SNAPSHOT + jar + + Stripe + Demo project for Stripe API + + + org.springframework.boot + spring-boot-starter-parent + 1.5.2.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + org.projectlombok + lombok + 1.16.16 + + + + com.stripe + stripe-java + 4.2.0 + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/stripe/src/main/java/com/baeldung/stripe/ChargeController.java b/stripe/src/main/java/com/baeldung/stripe/ChargeController.java new file mode 100644 index 000000000000..cd3f057cd804 --- /dev/null +++ b/stripe/src/main/java/com/baeldung/stripe/ChargeController.java @@ -0,0 +1,38 @@ +package com.baeldung.stripe; + +import com.baeldung.stripe.ChargeRequest.Currency; +import com.stripe.exception.StripeException; +import com.stripe.model.Charge; +import lombok.extern.java.Log; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RequestMapping; +import static org.springframework.web.bind.annotation.RequestMethod.POST; + +@Log +@Controller +public class ChargeController { + + @Autowired + StripeService paymentsService; + + @RequestMapping(value = "/charge", method = POST) + public String charge(ChargeRequest chargeRequest, Model model) throws StripeException { + chargeRequest.setDescription("Example charge"); + chargeRequest.setCurrency(Currency.EUR); + Charge charge = paymentsService.charge(chargeRequest); + model.addAttribute("id", charge.getId()); + model.addAttribute("status", charge.getStatus()); + model.addAttribute("chargeId", charge.getId()); + model.addAttribute("balance_transaction", charge.getBalanceTransaction()); + return "result"; + } + + @ExceptionHandler(StripeException.class) + public String handleError(Model model, StripeException ex) { + model.addAttribute("error", ex.getMessage()); + return "result"; + } +} diff --git a/stripe/src/main/java/com/baeldung/stripe/ChargeRequest.java b/stripe/src/main/java/com/baeldung/stripe/ChargeRequest.java new file mode 100644 index 000000000000..76cdea7bb429 --- /dev/null +++ b/stripe/src/main/java/com/baeldung/stripe/ChargeRequest.java @@ -0,0 +1,20 @@ +package com.baeldung.stripe; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@Getter +@Setter +@ToString(includeFieldNames = false) +public class ChargeRequest { + + public enum Currency { + EUR, USD; + } + private String description; + private int amount; // cents + private Currency currency; + private String stripeEmail; + private String stripeToken; +} diff --git a/stripe/src/main/java/com/baeldung/stripe/CheckoutController.java b/stripe/src/main/java/com/baeldung/stripe/CheckoutController.java new file mode 100644 index 000000000000..2d525843b0ae --- /dev/null +++ b/stripe/src/main/java/com/baeldung/stripe/CheckoutController.java @@ -0,0 +1,21 @@ +package com.baeldung.stripe; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; + +@Controller +public class CheckoutController { + + @Value("${STRIPE_PUBLIC_KEY}") + private String stripePublicKey; + + @RequestMapping("/checkout") + public String checkout(Model model) { + model.addAttribute("amount", 50 * 100); // in cents + model.addAttribute("stripePublicKey", stripePublicKey); + model.addAttribute("currency", ChargeRequest.Currency.EUR); + return "checkout"; + } +} diff --git a/stripe/src/main/java/com/baeldung/stripe/StripeApplication.java b/stripe/src/main/java/com/baeldung/stripe/StripeApplication.java new file mode 100644 index 000000000000..735c67dda5e7 --- /dev/null +++ b/stripe/src/main/java/com/baeldung/stripe/StripeApplication.java @@ -0,0 +1,12 @@ +package com.baeldung.stripe; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class StripeApplication { + + public static void main(String[] args) { + SpringApplication.run(StripeApplication.class, args); + } +} diff --git a/stripe/src/main/java/com/baeldung/stripe/StripeService.java b/stripe/src/main/java/com/baeldung/stripe/StripeService.java new file mode 100644 index 000000000000..8784b604c25b --- /dev/null +++ b/stripe/src/main/java/com/baeldung/stripe/StripeService.java @@ -0,0 +1,36 @@ +package com.baeldung.stripe; + +import com.stripe.Stripe; +import com.stripe.exception.APIConnectionException; +import com.stripe.exception.APIException; +import com.stripe.exception.AuthenticationException; +import com.stripe.exception.CardException; +import com.stripe.exception.InvalidRequestException; +import com.stripe.model.Charge; +import java.util.HashMap; +import java.util.Map; +import javax.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Service +public class StripeService { + + @Value("${STRIPE_SECRET_KEY}") + String secretKey; + + @PostConstruct + public void init() { + Stripe.apiKey = secretKey; + } + + public Charge charge(ChargeRequest chargeRequest) + throws AuthenticationException, InvalidRequestException, APIConnectionException, CardException, APIException { + Map chargeParams = new HashMap<>(); + chargeParams.put("amount", chargeRequest.getAmount()); + chargeParams.put("currency", chargeRequest.getCurrency()); + chargeParams.put("description", chargeRequest.getDescription()); + chargeParams.put("source", chargeRequest.getStripeToken()); + return Charge.create(chargeParams); + } +} diff --git a/stripe/src/main/resources/static/index.html b/stripe/src/main/resources/static/index.html new file mode 100644 index 000000000000..090a01e91d1b --- /dev/null +++ b/stripe/src/main/resources/static/index.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/stripe/src/main/resources/templates/checkout.html b/stripe/src/main/resources/templates/checkout.html new file mode 100644 index 000000000000..4af336ce9991 --- /dev/null +++ b/stripe/src/main/resources/templates/checkout.html @@ -0,0 +1,35 @@ + + + + Checkout + + + +
+ + + + +
+ + diff --git a/stripe/src/main/resources/templates/result.html b/stripe/src/main/resources/templates/result.html new file mode 100644 index 000000000000..57f02b74a4e2 --- /dev/null +++ b/stripe/src/main/resources/templates/result.html @@ -0,0 +1,17 @@ + + + + Result + + +

+
+

Success!

+
Id.:
+
Status:
+
Charge id.:
+
Balance transaction id.:
+
+ Checkout again + + diff --git a/struts2/README.md b/struts2/README.md new file mode 100644 index 000000000000..8a1425ccb558 --- /dev/null +++ b/struts2/README.md @@ -0,0 +1,3 @@ +### Relevant articles + +- [A Quick Struts 2 Intro](http://www.baeldung.com/struts-2-intro) diff --git a/struts2/pom.xml b/struts2/pom.xml index 25a374549d98..e9c321f908bb 100644 --- a/struts2/pom.xml +++ b/struts2/pom.xml @@ -2,10 +2,11 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 com.baeldung - MyStrutsApp + struts 0.0.1-SNAPSHOT pom struts + src/main/java @@ -13,6 +14,7 @@ src/main/resources + maven-compiler-plugin @@ -61,6 +63,12 @@ org.springframework spring-core 4.3.6.RELEASE + + + commons-logging + commons-logging + +
\ No newline at end of file diff --git a/testng/README.md b/testng/README.md new file mode 100644 index 000000000000..0e3586c9f335 --- /dev/null +++ b/testng/README.md @@ -0,0 +1,3 @@ +### Relevant articles + +- [Introduction to TestNG](http://www.baeldung.com/testng) diff --git a/testng/pom.xml b/testng/pom.xml index 272b1d316d32..83b32bb84da6 100644 --- a/testng/pom.xml +++ b/testng/pom.xml @@ -1,115 +1,121 @@ - - - 4.0.0 - com.baeldung - testng - 0.1.0-SNAPSHOT - jar - testng - - - - - - - org.slf4j - slf4j-api - ${org.slf4j.version} - - - ch.qos.logback - logback-classic - ${logback.version} - - - - - - org.testng - testng - ${testng.version} - test - - - - - - testng - - - src/main/resources - true - - - - - src/main/resources - true - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - ${maven-compiler-plugin.version} - - 1.8 - 1.8 - - - - - org.apache.maven.plugins - maven-surefire-plugin - ${maven-surefire-plugin.version} - - - **/*IntegrationTest.java - **/*LongRunningUnitTest.java - **/*ManualTest.java - - true - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - copy-dependencies - prepare-package - - copy-dependencies - - - ${project.build.directory}/libs - - - - - - - - - - - - 1.7.21 - 1.1.7 - - - 6.10 - - - 3.6.0 - 2.19.1 - - - - + + + 4.0.0 + com.baeldung + testng + 0.1.0-SNAPSHOT + jar + testng + + + + + + + org.slf4j + slf4j-api + ${org.slf4j.version} + + + ch.qos.logback + logback-classic + ${logback.version} + + + + + + org.testng + testng + ${testng.version} + test + + + + + + testng + + + src/main/resources + true + + + + + src/main/resources + true + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + 1.8 + 1.8 + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + **/*IntegrationTest.java + **/*LongRunningUnitTest.java + **/*ManualTest.java + + + src\test\resources\parametrized_testng.xml + src\test\resources\test_group.xml + src\test\resources\test_setup.xml + src\test\resources\test_suite.xml + + + true + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-dependencies + prepare-package + + copy-dependencies + + + ${project.build.directory}/libs + + + + + + + + + + + + 1.7.21 + 1.1.7 + + + 6.10 + + + 3.6.0 + 2.19.1 + + + + \ No newline at end of file diff --git a/testng/src/test/java/baeldung/com/DependentTests.java b/testng/src/test/java/baeldung/com/DependentTests.java index b60f8a6c0d99..8d3bbfc11b05 100644 --- a/testng/src/test/java/baeldung/com/DependentTests.java +++ b/testng/src/test/java/baeldung/com/DependentTests.java @@ -1,25 +1,25 @@ -package baeldung.com; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.Assert; -import org.testng.annotations.Test; - -public class DependentTests { - - private static final Logger LOGGER = LoggerFactory.getLogger(DependentTests.class); - - private String email = "abc@qwe.com"; - - @Test - public void givenEmail_ifValid_thenTrue() { - boolean valid = email.contains("@"); - Assert.assertEquals(valid, true); - } - - @Test(dependsOnMethods = {"givenEmail_ifValid_thenTrue"}) - public void givenValidEmail_whenLoggedIn_thenTrue() { - LOGGER.info("Email {} valid >> logging in", email); - } -} - +package baeldung.com; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class DependentTests { + + private static final Logger LOGGER = LoggerFactory.getLogger(DependentTests.class); + + private String email = "abc@qwe.com"; + + @Test + public void givenEmail_ifValid_thenTrue() { + boolean valid = email.contains("@"); + Assert.assertEquals(valid, true); + } + + @Test(dependsOnMethods = {"givenEmail_ifValid_thenTrue"}) + public void givenValidEmail_whenLoggedIn_thenTrue() { + LOGGER.info("Email {} valid >> logging in", email); + } +} + diff --git a/testng/src/test/java/baeldung/com/MultiThreadedTests.java b/testng/src/test/java/baeldung/com/MultiThreadedTests.java new file mode 100644 index 000000000000..4476eaa7a0ae --- /dev/null +++ b/testng/src/test/java/baeldung/com/MultiThreadedTests.java @@ -0,0 +1,14 @@ +package baeldung.com; + +import org.testng.Assert; +import org.testng.annotations.Test; + +public class MultiThreadedTests { + + @Test(threadPoolSize = 5, invocationCount = 10, timeOut = 1000) + public void givenMethod_whenRunInThreads_thenCorrect() { + int count = Thread.activeCount(); + Assert.assertTrue(count > 1); + } + +} diff --git a/testng/src/test/java/baeldung/com/ParametrizedTests.java b/testng/src/test/java/baeldung/com/ParametrizedTests.java index d3813f538229..693a1b5cdea6 100644 --- a/testng/src/test/java/baeldung/com/ParametrizedTests.java +++ b/testng/src/test/java/baeldung/com/ParametrizedTests.java @@ -1,77 +1,77 @@ -package baeldung.com; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Parameters; -import org.testng.annotations.Test; - -public class ParametrizedTests { - - private static final Logger LOGGER = LoggerFactory.getLogger(ParametrizedTests.class); - - @Test - @Parameters({"value", "isEven"}) - public void givenNumberFromXML_ifEvenCheckOK_thenCorrect(int value, boolean isEven) { - Assert.assertEquals(isEven, value % 2 == 0); - } - - @DataProvider(name = "numbers") - public static Object[][] evenNumbers() { - return new Object[][]{{1, false}, {2, true}, {4, true}}; - } - - @Test(dataProvider = "numbers") - public void givenNumberFromDataProvider_ifEvenCheckOK_thenCorrect(Integer number, boolean expected) { - Assert.assertEquals(expected, number % 2 == 0); - } - - @Test(dataProvider = "numbersObject") - public void givenNumberObjectFromDataProvider_ifEvenCheckOK_thenCorrect(EvenNumber number) { - Assert.assertEquals(number.isEven(), number.getValue() % 2 == 0); - } - - @DataProvider(name = "numbersObject") - public Object[][] parameterProvider() { - return new Object[][]{{new EvenNumber(1, false)}, {new EvenNumber(2, true)}, {new EvenNumber(4, true),}}; - } - -} - - -class EvenNumber { - private int value; - private boolean isEven; - - public EvenNumber(int number, boolean isEven) { - this.value = number; - this.isEven = isEven; - } - - public int getValue() { - return value; - } - - public void setValue(int value) { - this.value = value; - } - - public boolean isEven() { - return isEven; - } - - public void setEven(boolean even) { - isEven = even; - } - - @Override - public String toString() { - return "EvenNumber{" + - "value=" + value + - ", isEven=" + isEven + - '}'; - } -} - - +package baeldung.com; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Parameters; +import org.testng.annotations.Test; + +public class ParametrizedTests { + + private static final Logger LOGGER = LoggerFactory.getLogger(ParametrizedTests.class); + + @Test + @Parameters({"value", "isEven"}) + public void givenNumberFromXML_ifEvenCheckOK_thenCorrect(int value, boolean isEven) { + Assert.assertEquals(isEven, value % 2 == 0); + } + + @DataProvider(name = "numbers") + public static Object[][] evenNumbers() { + return new Object[][]{{1, false}, {2, true}, {4, true}}; + } + + @Test(dataProvider = "numbers") + public void givenNumberFromDataProvider_ifEvenCheckOK_thenCorrect(Integer number, boolean expected) { + Assert.assertEquals(expected, number % 2 == 0); + } + + @Test(dataProvider = "numbersObject") + public void givenNumberObjectFromDataProvider_ifEvenCheckOK_thenCorrect(EvenNumber number) { + Assert.assertEquals(number.isEven(), number.getValue() % 2 == 0); + } + + @DataProvider(name = "numbersObject") + public Object[][] parameterProvider() { + return new Object[][]{{new EvenNumber(1, false)}, {new EvenNumber(2, true)}, {new EvenNumber(4, true),}}; + } + +} + + +class EvenNumber { + private int value; + private boolean isEven; + + public EvenNumber(int number, boolean isEven) { + this.value = number; + this.isEven = isEven; + } + + public int getValue() { + return value; + } + + public void setValue(int value) { + this.value = value; + } + + public boolean isEven() { + return isEven; + } + + public void setEven(boolean even) { + isEven = even; + } + + @Override + public String toString() { + return "EvenNumber{" + + "value=" + value + + ", isEven=" + isEven + + '}'; + } +} + + diff --git a/testng/src/test/java/baeldung/com/PriorityTest.java b/testng/src/test/java/baeldung/com/PriorityTest.java new file mode 100644 index 000000000000..d014d5c9201d --- /dev/null +++ b/testng/src/test/java/baeldung/com/PriorityTest.java @@ -0,0 +1,21 @@ +package baeldung.com; + +import org.testng.Assert; +import org.testng.annotations.Test; + +public class PriorityTest { + + private String testString = "10"; + private int testInt = 23; + + @Test(priority = 1) + public void givenString_whenChangedToInt_thenCorrect() { + Assert.assertTrue(Integer.valueOf(testString) instanceof Integer); + } + + @Test(priority = 2) + public void givenInt_whenChangedToString_thenCorrect() { + Assert.assertTrue(String.valueOf(testInt) instanceof String); + } + +} diff --git a/testng/src/test/java/baeldung/com/RegistrationTest.java b/testng/src/test/java/baeldung/com/RegistrationTest.java index ec551d9c27ac..e9d744902b0b 100644 --- a/testng/src/test/java/baeldung/com/RegistrationTest.java +++ b/testng/src/test/java/baeldung/com/RegistrationTest.java @@ -1,14 +1,14 @@ -package baeldung.com; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.annotations.Test; - -public class RegistrationTest { - private static final Logger LOGGER = LoggerFactory.getLogger(RegistrationTest.class); - - @Test - public void whenCalledFromSuite_thanOK() { - LOGGER.info("Registration successful"); - } -} +package baeldung.com; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.Test; + +public class RegistrationTest { + private static final Logger LOGGER = LoggerFactory.getLogger(RegistrationTest.class); + + @Test + public void whenCalledFromSuite_thanOK() { + LOGGER.info("Registration successful"); + } +} diff --git a/testng/src/test/java/baeldung/com/SignInTest.java b/testng/src/test/java/baeldung/com/SignInTest.java index f0547374d138..d5cad78de018 100644 --- a/testng/src/test/java/baeldung/com/SignInTest.java +++ b/testng/src/test/java/baeldung/com/SignInTest.java @@ -1,14 +1,14 @@ -package baeldung.com; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.annotations.Test; - -public class SignInTest { - private static final Logger LOGGER = LoggerFactory.getLogger(SignInTest.class); - - @Test - public void whenCalledFromSuite_thanOK() { - LOGGER.info("SignIn successful"); - } -} +package baeldung.com; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.Test; + +public class SignInTest { + private static final Logger LOGGER = LoggerFactory.getLogger(SignInTest.class); + + @Test + public void whenCalledFromSuite_thanOK() { + LOGGER.info("SignIn successful"); + } +} diff --git a/testng/src/test/java/baeldung/com/SimpleTest.java b/testng/src/test/java/baeldung/com/SimpleTest.java new file mode 100644 index 000000000000..24bf3a6f0185 --- /dev/null +++ b/testng/src/test/java/baeldung/com/SimpleTest.java @@ -0,0 +1,28 @@ +package baeldung.com; + +import org.testng.Assert; +import org.testng.TestNG; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class SimpleTest extends TestNG { + private int number; + + @BeforeClass + public void setup() { + number = 12; + } + + @AfterClass + public void tearDown() { + number = 0; + } + + @Test + public void givenNumber_whenEven_thenTrue() { + Assert.assertTrue(number % 2 == 0); + } + +} + diff --git a/testng/src/test/java/baeldung/com/SummationServiceTest.java b/testng/src/test/java/baeldung/com/SummationServiceTest.java index f377a009df24..ceed53ca428a 100644 --- a/testng/src/test/java/baeldung/com/SummationServiceTest.java +++ b/testng/src/test/java/baeldung/com/SummationServiceTest.java @@ -1,98 +1,98 @@ -package baeldung.com; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.Assert; -import org.testng.TestNG; -import org.testng.annotations.*; - -import java.util.ArrayList; -import java.util.List; - -public class SummationServiceTest extends TestNG { - private static final Logger LOGGER = LoggerFactory.getLogger(DependentTests.class); - - private List numbers; - - private int testCount = 0; - - @BeforeClass - public void initialize() { - numbers = new ArrayList<>(); - } - - @AfterClass - public void tearDown() { - numbers = null; - } - - @BeforeSuite(groups = "regression") - public void runBeforeRegressionSuite() { - numbers = new ArrayList<>(); - numbers.add(-11); - numbers.add(2); - } - - @AfterSuite(groups = "regression") - public void runAfterRegressionSuite() { - numbers = null; - } - - @BeforeGroups("negative_tests") - public void runBeforeEachNegativeGroup() { - numbers.clear(); - } - - @BeforeGroups("regression") - public void runBeforeEachRegressionGroup() { - numbers.add(-11); - numbers.add(2); - } - - @BeforeGroups("positive_tests") - public void runBeforeEachPositiveGroup() { - numbers.add(1); - numbers.add(2); - numbers.add(3); - } - - @AfterGroups("positive_tests,regression,negative_tests") - public void runAfterEachGroup() { - numbers.clear(); - } - - @BeforeMethod - public void runBeforeEachTest() { - testCount++; - } - - @AfterMethod - public void runAfterEachTest() { - - } - - - @Test(groups = "positive_tests", enabled = false) - public void givenNumbers_sumEquals_thenCorrect() { - int sum = numbers.stream().reduce(0, Integer::sum); - Assert.assertEquals(sum, 6); - } - - @Test(groups = "negative_tests") - public void givenEmptyList_sumEqualsZero_thenCorrect() { - int sum = numbers.stream().reduce(0, Integer::sum); - Assert.assertEquals(0, sum); - } - - @Test(groups = "regression") - public void givenNegativeNumber_sumLessthanZero_thenCorrect() { - int sum = numbers.stream().reduce(0, Integer::sum); - Assert.assertTrue(sum < 0); - } - - @Test(expectedExceptions = ArithmeticException.class) - public void givenNumber_whenThrowsException_thenCorrect() { - int i = 1 / 0; - } - -} +package baeldung.com; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.TestNG; +import org.testng.annotations.*; + +import java.util.ArrayList; +import java.util.List; + +public class SummationServiceTest extends TestNG { + private static final Logger LOGGER = LoggerFactory.getLogger(DependentTests.class); + + private List numbers; + + private int testCount = 0; + + @BeforeClass + public void initialize() { + numbers = new ArrayList<>(); + } + + @AfterClass + public void tearDown() { + numbers = null; + } + + @BeforeSuite(groups = "regression") + public void runBeforeRegressionSuite() { + numbers = new ArrayList<>(); + numbers.add(-11); + numbers.add(2); + } + + @AfterSuite(groups = "regression") + public void runAfterRegressionSuite() { + numbers = null; + } + + @BeforeGroups("negative_tests") + public void runBeforeEachNegativeGroup() { + numbers.clear(); + } + + @BeforeGroups("regression") + public void runBeforeEachRegressionGroup() { + numbers.add(-11); + numbers.add(2); + } + + @BeforeGroups("positive_tests") + public void runBeforeEachPositiveGroup() { + numbers.add(1); + numbers.add(2); + numbers.add(3); + } + + @AfterGroups("positive_tests,regression,negative_tests") + public void runAfterEachGroup() { + numbers.clear(); + } + + @BeforeMethod + public void runBeforeEachTest() { + testCount++; + } + + @AfterMethod + public void runAfterEachTest() { + + } + + + @Test(groups = "positive_tests", enabled = false) + public void givenNumbers_sumEquals_thenCorrect() { + int sum = numbers.stream().reduce(0, Integer::sum); + Assert.assertEquals(sum, 6); + } + + @Test(groups = "negative_tests") + public void givenEmptyList_sumEqualsZero_thenCorrect() { + int sum = numbers.stream().reduce(0, Integer::sum); + Assert.assertEquals(0, sum); + } + + @Test(groups = "regression") + public void givenNegativeNumber_sumLessthanZero_thenCorrect() { + int sum = numbers.stream().reduce(0, Integer::sum); + Assert.assertTrue(sum < 0); + } + + @Test(expectedExceptions = ArithmeticException.class) + public void givenNumber_whenThrowsException_thenCorrect() { + int i = 1 / 0; + } + +} diff --git a/testng/src/test/java/baeldung/com/TestGroup.java b/testng/src/test/java/baeldung/com/TestGroup.java index 08bb5c996eea..a592000bed35 100644 --- a/testng/src/test/java/baeldung/com/TestGroup.java +++ b/testng/src/test/java/baeldung/com/TestGroup.java @@ -1,44 +1,44 @@ -package baeldung.com; - -import org.testng.annotations.AfterGroups; -import org.testng.annotations.BeforeGroups; -import org.testng.annotations.Test; - -public class TestGroup { - - @BeforeGroups("database") - public void setupDB() { - System.out.println("setupDB()"); - } - - @AfterGroups("database") - public void cleanDB() { - System.out.println("cleanDB()"); - } - - @Test(groups= "selenium-test") - public void runSelenium() { - System.out.println("runSelenium()"); - } - - @Test(groups= "selenium-test") - public void runSelenium1() { - System.out.println("runSelenium()1"); - } - - @Test(groups = "database") - public void testConnectOracle() { - System.out.println("testConnectOracle()"); - } - - @Test(groups = "database") - public void testConnectMsSQL() { - System.out.println("testConnectMsSQL"); - } - - @Test(dependsOnGroups = {"database","selenium-test"}) - public void runFinal() { - System.out.println("runFinal"); - } - +package baeldung.com; + +import org.testng.annotations.AfterGroups; +import org.testng.annotations.BeforeGroups; +import org.testng.annotations.Test; + +public class TestGroup { + + @BeforeGroups("database") + public void setupDB() { + System.out.println("setupDB()"); + } + + @AfterGroups("database") + public void cleanDB() { + System.out.println("cleanDB()"); + } + + @Test(groups = "selenium-test") + public void runSelenium() { + System.out.println("runSelenium()"); + } + + @Test(groups = "selenium-test") + public void runSelenium1() { + System.out.println("runSelenium()1"); + } + + @Test(groups = "database") + public void testConnectOracle() { + System.out.println("testConnectOracle()"); + } + + @Test(groups = "database") + public void testConnectMsSQL() { + System.out.println("testConnectMsSQL"); + } + + @Test(dependsOnGroups = {"database", "selenium-test"}) + public void runFinal() { + System.out.println("runFinal"); + } + } \ No newline at end of file diff --git a/testng/src/test/java/baeldung/com/TimeOutTest.java b/testng/src/test/java/baeldung/com/TimeOutTest.java index d54a914e0811..f84367a972ca 100644 --- a/testng/src/test/java/baeldung/com/TimeOutTest.java +++ b/testng/src/test/java/baeldung/com/TimeOutTest.java @@ -1,11 +1,11 @@ -package baeldung.com; - -import org.testng.annotations.Test; - -public class TimeOutTest { - - @Test(timeOut = 1000, enabled = false) - public void givenExecution_takeMoreTime_thenFail() { - while (true) ; - } -} +package baeldung.com; + +import org.testng.annotations.Test; + +public class TimeOutTest { + + @Test(timeOut = 1000, enabled = false) + public void givenExecution_takeMoreTime_thenFail() { + while (true) ; + } +} diff --git a/testng/src/test/java/com/baeldung/reports/CustomisedListener.java b/testng/src/test/java/com/baeldung/reports/CustomisedListener.java new file mode 100644 index 000000000000..6dc3991e503c --- /dev/null +++ b/testng/src/test/java/com/baeldung/reports/CustomisedListener.java @@ -0,0 +1,67 @@ +package com.baeldung.reports; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.ITestContext; +import org.testng.ITestListener; +import org.testng.ITestResult; + +public class CustomisedListener implements ITestListener { + private static final Logger LOGGER = LoggerFactory.getLogger("TEST_REPORT"); + + @Override + public void onFinish(ITestContext context) { + LOGGER.info("PASSED TEST CASES"); + context.getPassedTests() + .getAllResults() + .stream() + .forEach(result -> { + LOGGER.info(result.getName()); + }); + LOGGER.info("FAILED TEST CASES"); + context.getFailedTests() + .getAllResults() + .stream() + .forEach(result -> { + LOGGER.info(result.getName()); + }); + LOGGER.info("Test completed on: " + context.getEndDate().toString()); + } + + @Override + public void onStart(ITestContext arg0) { + LOGGER.info("Started testing on: " + arg0.getStartDate() + .toString()); + } + + @Override + public void onTestFailedButWithinSuccessPercentage(ITestResult arg0) { + // TODO Auto-generated method stub + + } + + @Override + public void onTestFailure(ITestResult arg0) { + LOGGER.info("Failed : " + arg0.getName()); + + } + + @Override + public void onTestSkipped(ITestResult arg0) { + LOGGER.info("Skipped Test: " + arg0.getName()); + + } + + @Override + public void onTestStart(ITestResult arg0) { + LOGGER.info("Testing: " + arg0.getName() + " " + arg0.getStartMillis()); + + } + + @Override + public void onTestSuccess(ITestResult arg0) { + LOGGER.info("Tested: " + arg0.getName() + " " + arg0.getEndMillis()); + + } + +} diff --git a/testng/src/test/java/com/baeldung/reports/CustomisedReports.java b/testng/src/test/java/com/baeldung/reports/CustomisedReports.java new file mode 100644 index 000000000000..07ba2162cc7a --- /dev/null +++ b/testng/src/test/java/com/baeldung/reports/CustomisedReports.java @@ -0,0 +1,50 @@ +package com.baeldung.reports; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.IReporter; +import org.testng.ISuite; +import org.testng.ISuiteResult; +import org.testng.ITestContext; +import org.testng.xml.XmlSuite; + +import java.util.List; +import java.util.Map; + +public class CustomisedReports implements IReporter { + private static final Logger LOGGER = LoggerFactory.getLogger("TEST_REPORT"); + + @Override + public void generateReport(List xmlSuites, List suites, String outputDirectory) { + + suites.stream() + .forEach(suite -> { + String suiteName = suite.getName(); + Map suiteResults = suite.getResults(); + suiteResults.values() + .stream() + .forEach(result -> { + ITestContext context + = ((ISuiteResult) result).getTestContext(); + + LOGGER.info("Passed tests for suite '" + + suiteName + "' is:" + + context.getPassedTests() + .getAllResults() + .size()); + LOGGER.info("Failed tests for suite '" + + suiteName + "' is:" + + context.getFailedTests() + .getAllResults() + .size()); + LOGGER.info("Skipped tests for suite '" + + suiteName + "' is:" + + context.getSkippedTests() + .getAllResults() + .size()); + }); + }); + + } + +} diff --git a/testng/src/test/resources/logback.xml b/testng/src/test/resources/logback.xml index e9ae1894a660..035520aa15e9 100644 --- a/testng/src/test/resources/logback.xml +++ b/testng/src/test/resources/logback.xml @@ -1,14 +1,19 @@ - - - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - - - - - - + + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + + + + + + + + + + + \ No newline at end of file diff --git a/testng/src/test/resources/parametrized_test.xml b/testng/src/test/resources/parametrized_testng.xml similarity index 74% rename from testng/src/test/resources/parametrized_test.xml rename to testng/src/test/resources/parametrized_testng.xml index 932af30e4e4b..c8ec7209f893 100644 --- a/testng/src/test/resources/parametrized_test.xml +++ b/testng/src/test/resources/parametrized_testng.xml @@ -1,10 +1,13 @@ - - - - - - - - - + + + + + + + + + + + + \ No newline at end of file diff --git a/testng/src/test/resources/test_group.xml b/testng/src/test/resources/test_group.xml index 26868375f218..77edb85cdc81 100644 --- a/testng/src/test/resources/test_group.xml +++ b/testng/src/test/resources/test_group.xml @@ -1,13 +1,13 @@ - - - - - - - - - - - - + + + + + + + + + + + + \ No newline at end of file diff --git a/testng/src/test/resources/test_setup.xml b/testng/src/test/resources/test_setup.xml index 7d9708193ea1..9197f9fd0ea9 100644 --- a/testng/src/test/resources/test_setup.xml +++ b/testng/src/test/resources/test_setup.xml @@ -1,17 +1,17 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/testng/src/test/resources/test_suite.xml b/testng/src/test/resources/test_suite.xml index 0fe5d1cc403d..4d0b17cbe832 100644 --- a/testng/src/test/resources/test_suite.xml +++ b/testng/src/test/resources/test_suite.xml @@ -1,9 +1,13 @@ - - - - - - - - + + + + + + + + + + + + \ No newline at end of file diff --git a/vertx/README.md b/vertx/README.md new file mode 100644 index 000000000000..e710df362d9e --- /dev/null +++ b/vertx/README.md @@ -0,0 +1,3 @@ +### Relevant articles + +- [Introduction to Vert.x](http://www.baeldung.com/vertx) diff --git a/vertx/pom.xml b/vertx/pom.xml new file mode 100644 index 000000000000..971a61d3366e --- /dev/null +++ b/vertx/pom.xml @@ -0,0 +1,111 @@ + + + 4.0.0 + + com.baeldung + parent-modules + 1.0.0-SNAPSHOT + + com.baeldung + vertx + 1.0-SNAPSHOT + vertx + http://maven.apache.org + + + junit + junit + 4.12 + test + + + + io.vertx + vertx-core + 3.0.0 + + + + io.vertx + vertx-web + 3.0.0 + + + + io.vertx + vertx-unit + 3.0.0 + test + + + + + + org.slf4j + slf4j-api + ${org.slf4j.version} + + + ch.qos.logback + logback-classic + ${logback.version} + + + + + + + maven-compiler-plugin + 3.3 + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-shade-plugin + 2.3 + + + package + + shade + + + + + + io.vertx.core.Starter + com.baeldung.SimpleServerVerticle + + + + + ${project.build.directory}/${project.artifactId}-${project.version}-app.jar + + + + + + + + + + + 1.7.21 + 1.1.7 + + + 6.10 + + + 3.6.0 + 2.19.1 + + + + diff --git a/vertx/src/main/java/com/baeldung/HelloVerticle.java b/vertx/src/main/java/com/baeldung/HelloVerticle.java new file mode 100644 index 000000000000..59baceb0d8b1 --- /dev/null +++ b/vertx/src/main/java/com/baeldung/HelloVerticle.java @@ -0,0 +1,28 @@ +package com.baeldung; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.vertx.core.AbstractVerticle; +import io.vertx.core.Future; +import io.vertx.core.Vertx; + +public class HelloVerticle extends AbstractVerticle { + private static final Logger LOGGER = LoggerFactory.getLogger(HelloVerticle.class); + + public static void main(String[] args) { + Vertx vertx = Vertx.vertx(); + vertx.deployVerticle(new HelloVerticle()); + } + + @Override + public void start(Future future) { + LOGGER.info("Welcome to Vertx"); + } + + @Override + public void stop() { + LOGGER.info("Shutting down application"); + } +} + diff --git a/vertx/src/main/java/com/baeldung/SimpleServerVerticle.java b/vertx/src/main/java/com/baeldung/SimpleServerVerticle.java new file mode 100644 index 000000000000..96aa058ce709 --- /dev/null +++ b/vertx/src/main/java/com/baeldung/SimpleServerVerticle.java @@ -0,0 +1,24 @@ +package com.baeldung; + +import io.vertx.core.AbstractVerticle; +import io.vertx.core.Future; + +public class SimpleServerVerticle extends AbstractVerticle { + + @Override + public void start(Future future) { + vertx.createHttpServer() + .requestHandler( + r -> r.response().end("Welcome to Vert.x Intro")) + .listen(config().getInteger("http.port", 8080), result -> { + if (result.succeeded()) { + future.complete(); + } else { + future.fail(result.cause()); + } + }); + } + +} + + diff --git a/vertx/src/main/java/com/baeldung/model/Article.java b/vertx/src/main/java/com/baeldung/model/Article.java new file mode 100644 index 000000000000..9f1fdb8203dd --- /dev/null +++ b/vertx/src/main/java/com/baeldung/model/Article.java @@ -0,0 +1,59 @@ +package com.baeldung.model; + +public class Article { + private String id; + private String content; + private String author; + private String datePublished; + private int wordCount; + + public Article(String id, String content, String author, String datePublished, int wordCount) { + super(); + this.id = id; + this.content = content; + this.author = author; + this.datePublished = datePublished; + this.wordCount = wordCount; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getAuthor() { + return author; + } + + public void setAuthor(String author) { + this.author = author; + } + + public String getDatePublished() { + return datePublished; + } + + public void setDatePublished(String datePublished) { + this.datePublished = datePublished; + } + + public int getWordCount() { + return wordCount; + } + + public void setWordCount(int wordCount) { + this.wordCount = wordCount; + } + +} diff --git a/vertx/src/main/java/com/baeldung/rest/RestServiceVerticle.java b/vertx/src/main/java/com/baeldung/rest/RestServiceVerticle.java new file mode 100644 index 000000000000..1517a2f943c5 --- /dev/null +++ b/vertx/src/main/java/com/baeldung/rest/RestServiceVerticle.java @@ -0,0 +1,41 @@ +package com.baeldung.rest; + +import com.baeldung.model.Article; + +import io.vertx.core.AbstractVerticle; +import io.vertx.core.Future; +import io.vertx.core.json.Json; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.RoutingContext; + +public class RestServiceVerticle extends AbstractVerticle { + @Override + public void start(Future future) { + + Router router = Router.router(vertx); + router.get("/api/baeldung/articles/article/:id") + .handler(this::getArticles); + + vertx.createHttpServer() + .requestHandler(router::accept) + .listen(config().getInteger("http.port", 8080), result -> { + if (result.succeeded()) { + future.complete(); + } else { + future.fail(result.cause()); + } + }); + } + + private void getArticles(RoutingContext routingContext) { + String articleId = routingContext.request() + .getParam("id"); + Article article = new Article(articleId, "This is an intro to vertx", "baeldung", "01-02-2017", 1578); + + routingContext.response() + .putHeader("content-type", "application/json") + .setStatusCode(200) + .end(Json.encodePrettily(article)); + } + +} diff --git a/vertx/src/main/resources/conf/conf.json b/vertx/src/main/resources/conf/conf.json new file mode 100644 index 000000000000..4fa43ee64825 --- /dev/null +++ b/vertx/src/main/resources/conf/conf.json @@ -0,0 +1,3 @@ +{ + "http.port":8080 +} \ No newline at end of file diff --git a/vertx/src/main/resources/logback.xml b/vertx/src/main/resources/logback.xml new file mode 100644 index 000000000000..ec0dc2469ae0 --- /dev/null +++ b/vertx/src/main/resources/logback.xml @@ -0,0 +1,19 @@ + + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vertx/src/test/java/com/baeldung/RestServiceVerticleIntegrationTest.java b/vertx/src/test/java/com/baeldung/RestServiceVerticleIntegrationTest.java new file mode 100644 index 000000000000..40fa6c722048 --- /dev/null +++ b/vertx/src/test/java/com/baeldung/RestServiceVerticleIntegrationTest.java @@ -0,0 +1,46 @@ +package com.baeldung; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import com.baeldung.rest.RestServiceVerticle; + +import io.vertx.core.Vertx; +import io.vertx.ext.unit.Async; +import io.vertx.ext.unit.TestContext; +import io.vertx.ext.unit.junit.VertxUnitRunner; + +@RunWith(VertxUnitRunner.class) +public class RestServiceVerticleIntegrationTest { + + private Vertx vertx; + + @Before + public void setup(TestContext testContext) { + vertx = Vertx.vertx(); + + vertx.deployVerticle(RestServiceVerticle.class.getName(), testContext.asyncAssertSuccess()); + } + + @After + public void tearDown(TestContext testContext) { + vertx.close(testContext.asyncAssertSuccess()); + } + + @Test + public void givenId_whenReceivedArticle_thenSuccess(TestContext testContext) { + final Async async = testContext.async(); + + vertx.createHttpClient() + .getNow(8080, "localhost", "/api/baeldung/articles/article/12345", response -> { + response.handler(responseBody -> { + testContext.assertTrue(responseBody.toString() + .contains("\"id\" : \"12345\"")); + async.complete(); + }); + }); + } + +} diff --git a/vertx/src/test/java/com/baeldung/SimpleServerVerticleIntegrationTest.java b/vertx/src/test/java/com/baeldung/SimpleServerVerticleIntegrationTest.java new file mode 100644 index 000000000000..194f403e2538 --- /dev/null +++ b/vertx/src/test/java/com/baeldung/SimpleServerVerticleIntegrationTest.java @@ -0,0 +1,44 @@ +package com.baeldung; + +import io.vertx.core.Vertx; +import io.vertx.ext.unit.Async; +import io.vertx.ext.unit.TestContext; +import io.vertx.ext.unit.junit.VertxUnitRunner; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(VertxUnitRunner.class) +public class SimpleServerVerticleIntegrationTest { + private Vertx vertx; + + @Before + public void setup(TestContext testContext) { + vertx = Vertx.vertx(); + + vertx.deployVerticle(SimpleServerVerticle.class.getName(), testContext.asyncAssertSuccess()); + } + + @After + public void tearDown(TestContext testContext) { + vertx.close(testContext.asyncAssertSuccess()); + } + + @Test + public void whenReceivedResponse_thenSuccess(TestContext testContext) { + final Async async = testContext.async(); + + vertx + .createHttpClient() + .getNow(8080, "localhost", "/", + response -> response.handler(responseBody -> { + testContext.assertTrue(responseBody + .toString() + .contains("Welcome")); + async.complete(); + })); + } + +} + diff --git a/video-tutorials/jackson-annotations/pom.xml b/video-tutorials/jackson-annotations/pom.xml index e321544d4e47..c4c66ddb440e 100644 --- a/video-tutorials/jackson-annotations/pom.xml +++ b/video-tutorials/jackson-annotations/pom.xml @@ -93,6 +93,12 @@ rest-assured 3.0.1 test + + + commons-logging + commons-logging + + @@ -151,12 +157,6 @@ ${org.slf4j.version} - - org.slf4j - log4j-over-slf4j - ${org.slf4j.version} - - diff --git a/xml/README.md b/xml/README.md index 8531f8b0639b..320089b0886a 100644 --- a/xml/README.md +++ b/xml/README.md @@ -1,2 +1,3 @@ ### Relevant Articles: - [Intro to XPath with Java](http://www.baeldung.com/java-xpath) +- [Introduction to JiBX](http://www.baeldung.com/jibx) diff --git a/xml/pom.xml b/xml/pom.xml index 4230350ed953..cc3da078f81c 100644 --- a/xml/pom.xml +++ b/xml/pom.xml @@ -7,6 +7,32 @@ xml + + + jibx.sf.net + JiBX repository + http://jibx.sf.net/maven2 + + never + + + false + + + + + + jibx.sf.net + JiBX repository + http://jibx.sf.net/maven2 + + never + + + false + + + @@ -46,6 +72,17 @@ ${commons-lang3.version} + + org.jibx + jibx-run + ${jibx-version} + + + commons-lang + commons-lang + ${commons-lang.version} + + @@ -74,8 +111,8 @@ maven-compiler-plugin ${maven-compiler-plugin.version} - 1.8 - 1.8 + ${java-version} + ${java-version} @@ -83,21 +120,207 @@ org.apache.maven.plugins maven-surefire-plugin ${maven-surefire-plugin.version} + + + CustomerTest.java + + + + + schemaGen + + + + org.jibx + maven-jibx-plugin + ${maven-jibx-plugin.version} + + + generate-java-code-from-schema + + schema-codegen + + + src/main/resources + + Order.xsd + + true + + + + compile-binding + + bind + + + target/generated-sources + true + true + + true + + + + generate-test-code-from-schema + generate-test-sources + + test-schema-codegen + + + + compile-test-binding + process-test-classes + + test-bind + + + target/generated-test-sources + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java-version} + ${java-version} + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + CustomerTest.java + + + + + + + + + + bindGen + + + + + org.jibx + maven-jibx-plugin + + + + + + org.jibx + maven-jibx-plugin + ${maven-jibx-plugin.version} + + src/main/resources + + *-binding.xml + + + template-binding.xml + + true + + + + process-classes + + bind + + + + + + org.jibx + maven-jibx-plugin + 1.3.1 + + src/main/resources + + *-binding.xml + + true + + + + process-test-classes + + test-bind + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java-version} + ${java-version} + + + + + maven-assembly-plugin + + ${project.basedir} + + + jar-with-dependencies + + + + + + com.baeldung.xml.jibx.JiBXDemoApplication + + + + + + + make-assembly + package + + attached + + + + + + + + + + - 1.6.1 - 1.1.6 - 2.0.6 + 1.6.1 + 1.1.6 + 2.0.6 2.5 4.1 + 1.2.4.5 3.5 + 2.4 + 1.8 4.12 @@ -105,6 +328,7 @@ 3.6.0 2.19.1 + 1.3.1 diff --git a/xml/src/main/java/com/baeldung/xml/jibx/Customer.java b/xml/src/main/java/com/baeldung/xml/jibx/Customer.java new file mode 100644 index 000000000000..fabd464663ac --- /dev/null +++ b/xml/src/main/java/com/baeldung/xml/jibx/Customer.java @@ -0,0 +1,53 @@ +/* + * Customer.java 06.06.2008 + * + * Copyright 2008 Stefan Jäger + * + */ +package com.baeldung.xml.jibx; + +import org.apache.commons.lang.builder.ToStringBuilder; + +public class Customer { + private Person person; + private String city; + + private Phone homePhone; + private Phone officePhone; + + public Person getPerson() { + return person; + } + + public void setPerson(Person person) { + this.person = person; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public Phone getHomePhone() { + return homePhone; + } + + public void setHomePhone(Phone homePhone) { + this.homePhone = homePhone; + } + + public Phone getOfficePhone() { + return officePhone; + } + + public void setOfficePhone(Phone officePhone) { + this.officePhone = officePhone; + } + + public String toString() { + return ToStringBuilder.reflectionToString(this); + } +} diff --git a/xml/src/main/java/com/baeldung/xml/jibx/Identity.java b/xml/src/main/java/com/baeldung/xml/jibx/Identity.java new file mode 100644 index 000000000000..15aab4e4171c --- /dev/null +++ b/xml/src/main/java/com/baeldung/xml/jibx/Identity.java @@ -0,0 +1,14 @@ +package com.baeldung.xml.jibx; + +public class Identity { + + long customerId; + + public long getCustomerId() { + return customerId; + } + + public void setCustomerId(long customerId) { + this.customerId = customerId; + } +} diff --git a/xml/src/main/java/com/baeldung/xml/jibx/Person.java b/xml/src/main/java/com/baeldung/xml/jibx/Person.java new file mode 100644 index 000000000000..fbd7cde0a2ac --- /dev/null +++ b/xml/src/main/java/com/baeldung/xml/jibx/Person.java @@ -0,0 +1,25 @@ +/* + * Person.java 06.06.2008 + * + * Copyright 2008 Stefan Jäger + * + */ +package com.baeldung.xml.jibx; + +import org.apache.commons.lang.builder.ToStringBuilder; + +public class Person extends Identity { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String toString() { + return ToStringBuilder.reflectionToString(this); + } +} diff --git a/xml/src/main/java/com/baeldung/xml/jibx/Phone.java b/xml/src/main/java/com/baeldung/xml/jibx/Phone.java new file mode 100644 index 000000000000..783f2ee6fb3d --- /dev/null +++ b/xml/src/main/java/com/baeldung/xml/jibx/Phone.java @@ -0,0 +1,14 @@ +package com.baeldung.xml.jibx; + +public class Phone { + + private String number; + + public String getNumber() { + return number; + } + + public void setNumber(String number) { + this.number = number; + } +} diff --git a/xml/src/main/resources/Order.xsd b/xml/src/main/resources/Order.xsd new file mode 100644 index 000000000000..cbb5bc7596ba --- /dev/null +++ b/xml/src/main/resources/Order.xsd @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/xml/src/main/resources/customer-binding.xml b/xml/src/main/resources/customer-binding.xml new file mode 100644 index 000000000000..c1a80366efa5 --- /dev/null +++ b/xml/src/main/resources/customer-binding.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/xml/src/test/java/com/baeldung/xml/jibx/CustomerTest.java b/xml/src/test/java/com/baeldung/xml/jibx/CustomerTest.java new file mode 100644 index 000000000000..8b1427779992 --- /dev/null +++ b/xml/src/test/java/com/baeldung/xml/jibx/CustomerTest.java @@ -0,0 +1,52 @@ +package com.baeldung.xml.jibx; + +import org.jibx.runtime.BindingDirectory; +import org.jibx.runtime.IBindingFactory; +import org.jibx.runtime.IUnmarshallingContext; +import org.jibx.runtime.JiBXException; +import org.junit.Test; + +import java.io.FileNotFoundException; +import java.io.InputStream; + +import static junit.framework.Assert.assertEquals; + +public class CustomerTest { + + @Test + public void whenUnmarshalXML_ThenFieldsAreMapped() throws JiBXException, FileNotFoundException { + IBindingFactory bfact = BindingDirectory.getFactory(Customer.class); + IUnmarshallingContext uctx = bfact.createUnmarshallingContext(); + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + InputStream inputStream = classLoader.getResourceAsStream("Customer1.xml"); + Customer customer = (Customer) uctx.unmarshalDocument(inputStream, null); + + assertEquals("Stefan Jaegar", customer.getPerson().getName()); + assertEquals("Davos Dorf", customer.getCity()); + + } + + @Test + public void WhenUnmarshal_ThenMappingInherited() throws JiBXException, FileNotFoundException { + IBindingFactory bfact = BindingDirectory.getFactory(Customer.class); + IUnmarshallingContext uctx = bfact.createUnmarshallingContext(); + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + InputStream inputStream = classLoader.getResourceAsStream("Customer1.xml"); + Customer customer = (Customer) uctx.unmarshalDocument(inputStream, null); + + assertEquals(12345, customer.getPerson().getCustomerId()); + + } + + @Test + public void WhenUnmarshal_ThenPhoneMappingRead() throws JiBXException, FileNotFoundException { + IBindingFactory bfact = BindingDirectory.getFactory(Customer.class); + IUnmarshallingContext uctx = bfact.createUnmarshallingContext(); + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + InputStream inputStream = classLoader.getResourceAsStream("Customer1.xml"); + Customer customer = (Customer) uctx.unmarshalDocument(inputStream, null); + + assertEquals("234678", customer.getHomePhone().getNumber()); + + } +} diff --git a/xml/src/test/resources/Customer1.xml b/xml/src/test/resources/Customer1.xml new file mode 100644 index 000000000000..7f4fbc79af97 --- /dev/null +++ b/xml/src/test/resources/Customer1.xml @@ -0,0 +1,16 @@ + + + + 12345 + Stefan Jaeger + + + 234678 + + + + 234678 + + Davos Dorf + + \ No newline at end of file diff --git a/xmlunit2/pom.xml b/xmlunit2/pom.xml index d4364292d60d..3b659c49c15c 100644 --- a/xmlunit2/pom.xml +++ b/xmlunit2/pom.xml @@ -17,6 +17,16 @@ 1.8 + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*IntegrationTest.java + **/*LiveTest.java + + + @@ -55,6 +65,6 @@ 3.6.0 - + diff --git a/xstream/pom.xml b/xstream/pom.xml index 7af8efa65989..ee1882850a81 100644 --- a/xstream/pom.xml +++ b/xstream/pom.xml @@ -27,10 +27,23 @@ - log4j - log4j - ${log4j.version} + org.slf4j + slf4j-api + ${org.slf4j.version} + + + ch.qos.logback + logback-classic + ${logback.version} + + + + ch.qos.logback + logback-core + ${logback.version} + + @@ -51,7 +64,9 @@ 1.4.9 1.3.8 - 1.2.17 + 1.7.21 + 1.1.7 + 4.12 diff --git a/xstream/src/main/resources/log4j.properties b/xstream/src/main/resources/log4j.properties deleted file mode 100644 index 03d8c51aa07c..000000000000 --- a/xstream/src/main/resources/log4j.properties +++ /dev/null @@ -1,14 +0,0 @@ -# Root logger option -log4j.rootLogger=DEBUG, file -# Redirect log messages to console -# log4j.appender.stdout=org.apache.log4j.ConsoleAppender -# log4j.appender.stdout.Target=System.out -# log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -# log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n -# Redirect log messages to a log file, support file rolling. -log4j.appender.file=org.apache.log4j.RollingFileAppender -log4j.appender.file.File=D:\\Test\\xstream-application.log -log4j.appender.file.MaxFileSize=5MB -log4j.appender.file.MaxBackupIndex=10 -log4j.appender.file.layout=org.apache.log4j.PatternLayout -log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n \ No newline at end of file diff --git a/xstream/src/main/resources/logback.xml b/xstream/src/main/resources/logback.xml new file mode 100644 index 000000000000..ec0dc2469ae0 --- /dev/null +++ b/xstream/src/main/resources/logback.xml @@ -0,0 +1,19 @@ + + + + + web - %date [%thread] %-5level %logger{36} - %message%n + + + + + + + + + + + + + + \ No newline at end of file