Merge remote-tracking branch 'upstream/master' into tutorials/binaryTreeJava
This commit is contained in:
commit
9f3ce63cd9
|
@ -83,6 +83,24 @@
|
|||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.openjdk.jmh</groupId>
|
||||
<artifactId>jmh-core</artifactId>
|
||||
<version>1.19</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.openjdk.jmh</groupId>
|
||||
<artifactId>jmh-generator-annprocess</artifactId>
|
||||
<version>1.19</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.openjdk.jmh</groupId>
|
||||
<artifactId>jmh-generator-bytecode</artifactId>
|
||||
<version>1.19</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
package com.baeldung.counter;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
|
||||
import com.baeldung.counter.CounterUtil.MutableInteger;
|
||||
|
||||
@Fork(value = 1, warmups = 3)
|
||||
@BenchmarkMode(Mode.All)
|
||||
public class CounterStatistics {
|
||||
|
||||
private static final Map<String, Integer> counterMap = new HashMap<>();
|
||||
private static final Map<String, MutableInteger> counterWithMutableIntMap = new HashMap<>();
|
||||
private static final Map<String, int[]> counterWithIntArrayMap = new HashMap<>();
|
||||
private static final Map<String, Long> counterWithLongWrapperMap = new HashMap<>();
|
||||
|
||||
@Benchmark
|
||||
public void wrapperAsCounter() {
|
||||
CounterUtil.counterWithWrapperObject(counterMap);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void lambdaExpressionWithWrapper() {
|
||||
CounterUtil.counterWithLambdaAndWrapper(counterWithLongWrapperMap);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void mutableIntegerAsCounter() {
|
||||
CounterUtil.counterWithMutableInteger(counterWithMutableIntMap);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void primitiveArrayAsCounter() {
|
||||
CounterUtil.counterWithPrimitiveArray(counterWithIntArrayMap);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
org.openjdk.jmh.Main.main(args);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
package com.baeldung.counter;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.baeldung.counter.CounterUtil.MutableInteger;
|
||||
|
||||
public class CounterTest {
|
||||
|
||||
@Test
|
||||
public void whenMapWithWrapperAsCounter_runsSuccessfully() {
|
||||
Map<String, Integer> counterMap = new HashMap<>();
|
||||
CounterUtil.counterWithWrapperObject(counterMap);
|
||||
|
||||
assertEquals(3, counterMap.get("China")
|
||||
.intValue());
|
||||
assertEquals(2, counterMap.get("India")
|
||||
.intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenMapWithLambdaAndWrapperCounter_runsSuccessfully() {
|
||||
Map<String, Long> counterMap = new HashMap<>();
|
||||
CounterUtil.counterWithLambdaAndWrapper(counterMap);
|
||||
|
||||
assertEquals(3l, counterMap.get("China")
|
||||
.longValue());
|
||||
assertEquals(2l, counterMap.get("India")
|
||||
.longValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenMapWithMutableIntegerCounter_runsSuccessfully() {
|
||||
Map<String, MutableInteger> counterMap = new HashMap<>();
|
||||
CounterUtil.counterWithMutableInteger(counterMap);
|
||||
assertEquals(3, counterMap.get("China")
|
||||
.getCount());
|
||||
assertEquals(2, counterMap.get("India")
|
||||
.getCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenMapWithPrimitiveArray_runsSuccessfully() {
|
||||
Map<String, int[]> counterMap = new HashMap<>();
|
||||
CounterUtil.counterWithPrimitiveArray(counterMap);
|
||||
assertEquals(3, counterMap.get("China")[0]);
|
||||
assertEquals(2, counterMap.get("India")[0]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package com.baeldung.counter;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class CounterUtil {
|
||||
|
||||
private final static String[] COUNTRY_NAMES = { "China", "Australia", "India", "USA", "USSR", "UK", "China", "France", "Poland", "Austria", "India", "USA", "Egypt", "China" };
|
||||
|
||||
public static void counterWithWrapperObject(Map<String, Integer> counterMap) {
|
||||
for (String country : COUNTRY_NAMES) {
|
||||
counterMap.compute(country, (k, v) -> v == null ? 1 : v + 1);
|
||||
}
|
||||
}
|
||||
|
||||
public static void counterWithLambdaAndWrapper(Map<String, Long> counterMap) {
|
||||
counterMap.putAll(Stream.of(COUNTRY_NAMES)
|
||||
.parallel()
|
||||
.collect(Collectors.groupingBy(k -> k, Collectors.counting())));
|
||||
}
|
||||
|
||||
public static class MutableInteger {
|
||||
int count;
|
||||
|
||||
public MutableInteger(int count) {
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public void increment() {
|
||||
this.count++;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return this.count;
|
||||
}
|
||||
}
|
||||
|
||||
public static void counterWithMutableInteger(Map<String, MutableInteger> counterMap) {
|
||||
for (String country : COUNTRY_NAMES) {
|
||||
MutableInteger oldValue = counterMap.get(country);
|
||||
if (oldValue != null) {
|
||||
oldValue.increment();
|
||||
} else {
|
||||
counterMap.put(country, new MutableInteger(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void counterWithPrimitiveArray(Map<String, int[]> counterMap) {
|
||||
for (String country : COUNTRY_NAMES) {
|
||||
int[] oldCounter = counterMap.get(country);
|
||||
if (oldCounter != null) {
|
||||
oldCounter[0] += 1;
|
||||
} else {
|
||||
counterMap.put(country, new int[] { 1 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -9,7 +9,6 @@ import java.util.List;
|
|||
import java.util.concurrent.*;
|
||||
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class WaitingForThreadsToFinishTest {
|
||||
|
||||
|
@ -40,16 +39,13 @@ public class WaitingForThreadsToFinishTest {
|
|||
CountDownLatch latch = new CountDownLatch(2);
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
WORKER_THREAD_POOL.submit(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
latch.countDown();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
WORKER_THREAD_POOL.submit(() -> {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
latch.countDown();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -83,13 +79,9 @@ public class WaitingForThreadsToFinishTest {
|
|||
awaitTerminationAfterShutdown(WORKER_THREAD_POOL);
|
||||
|
||||
try {
|
||||
WORKER_THREAD_POOL.submit(new Callable<String>() {
|
||||
@Override
|
||||
public String call() throws Exception {
|
||||
fail("This thread should have been rejected !");
|
||||
Thread.sleep(1000000);
|
||||
return null;
|
||||
}
|
||||
WORKER_THREAD_POOL.submit((Callable<String>) () -> {
|
||||
Thread.sleep(1000000);
|
||||
return null;
|
||||
});
|
||||
} catch (RejectedExecutionException ex) {
|
||||
//
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
package com.baeldung.array;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
public class ArrayInverter {
|
||||
|
||||
public void invertUsingFor(Object[] array) {
|
||||
for (int i = 0; i < array.length / 2; i++) {
|
||||
Object temp = array[i];
|
||||
array[i] = array[array.length - 1 - i];
|
||||
array[array.length - 1 - i] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
public void invertUsingCollectionsReverse(Object[] array) {
|
||||
List<Object> list = Arrays.asList(array);
|
||||
Collections.reverse(list);
|
||||
}
|
||||
|
||||
public Object[] invertUsingStreams(final Object[] array) {
|
||||
return IntStream.range(1, array.length + 1).mapToObj(i -> array[array.length - i]).toArray();
|
||||
}
|
||||
|
||||
public void invertUsingCommonsLang(Object[] array) {
|
||||
ArrayUtils.reverse(array);
|
||||
}
|
||||
|
||||
public Object[] invertUsingGuava(Object[] array) {
|
||||
List<Object> list = Arrays.asList(array);
|
||||
List<Object> reverted = Lists.reverse(list);
|
||||
return reverted.toArray();
|
||||
}
|
||||
|
||||
}
|
|
@ -54,7 +54,7 @@ public class GenericFile {
|
|||
}
|
||||
|
||||
public String getFileInfo() {
|
||||
return "File Name: " + this.getName() + "\n" + "Extension: " + this.getExtension() + "\n" + "Date Created: " + this.getDateCreated() + "\n" + "Version: " + this.getVersion() + "\n";
|
||||
return String.format("File Name: %s\n" + " Extension: %s\n" + " Date Created: %s\n" + " Version: %s\n", this.getName(), this.getExtension(), this.getDateCreated(), this.getVersion());
|
||||
}
|
||||
|
||||
public Object read() {
|
||||
|
|
|
@ -30,7 +30,7 @@ public class ImageFile extends GenericFile {
|
|||
}
|
||||
|
||||
public String getFileInfo() {
|
||||
return super.getFileInfo() + "Height: " + this.getHeight() + "\n" + "Width: " + this.getWidth();
|
||||
return String.format(" %s Height: %d\n Width: %d", super.getFileInfo(), this.getHeight(), this.getWidth());
|
||||
}
|
||||
|
||||
public String read() {
|
||||
|
|
|
@ -21,7 +21,7 @@ public class TextFile extends GenericFile {
|
|||
}
|
||||
|
||||
public String getFileInfo() {
|
||||
return super.getFileInfo() + "Word Count: " + wordCount;
|
||||
return String.format(" %s Word Count: %d", super.getFileInfo(), wordCount);
|
||||
}
|
||||
|
||||
public String read() {
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package com.baeldung.array;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class ArrayInverterUnitTest {
|
||||
|
||||
private String[] fruits = { "apples", "tomatoes", "bananas", "guavas", "pineapples", "oranges" };
|
||||
|
||||
@Test
|
||||
public void invertArrayWithForLoop() {
|
||||
ArrayInverter inverter = new ArrayInverter();
|
||||
inverter.invertUsingFor(fruits);
|
||||
|
||||
assertThat(new String[] { "oranges", "pineapples", "guavas", "bananas", "tomatoes", "apples" }).isEqualTo(fruits);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invertArrayWithCollectionsReverse() {
|
||||
ArrayInverter inverter = new ArrayInverter();
|
||||
inverter.invertUsingCollectionsReverse(fruits);
|
||||
|
||||
assertThat(new String[] { "oranges", "pineapples", "guavas", "bananas", "tomatoes", "apples" }).isEqualTo(fruits);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invertArrayWithStreams() {
|
||||
ArrayInverter inverter = new ArrayInverter();
|
||||
|
||||
assertThat(new String[] { "oranges", "pineapples", "guavas", "bananas", "tomatoes", "apples" }).isEqualTo(inverter.invertUsingStreams(fruits));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invertArrayWithCommonsLang() {
|
||||
ArrayInverter inverter = new ArrayInverter();
|
||||
inverter.invertUsingCommonsLang(fruits);
|
||||
|
||||
assertThat(new String[] { "oranges", "pineapples", "guavas", "bananas", "tomatoes", "apples" }).isEqualTo(fruits);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invertArrayWithGuava() {
|
||||
ArrayInverter inverter = new ArrayInverter();
|
||||
|
||||
assertThat(new String[] { "oranges", "pineapples", "guavas", "bananas", "tomatoes", "apples" }).isEqualTo(inverter.invertUsingGuava(fruits));
|
||||
}
|
||||
|
||||
}
|
|
@ -14,9 +14,9 @@ public class NewOuter {
|
|||
public void run() {
|
||||
System.out.println("a = " + a);
|
||||
System.out.println("b = " + b);
|
||||
System.out.println("NewOuterTest.this.a = " + NewOuter.this.a);
|
||||
System.out.println("NewOuterTest.b = " + NewOuter.b);
|
||||
System.out.println("NewOuterTest.this.b = " + NewOuter.this.b);
|
||||
System.out.println("NewOuter.this.a = " + NewOuter.this.a);
|
||||
System.out.println("NewOuter.b = " + NewOuter.b);
|
||||
System.out.println("NewOuter.this.b = " + NewOuter.this.b);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,6 @@ package com.baeldung.polymorphism;
|
|||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
public class PolymorphismUnitTest {
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
package com.baeldung.kotlin
|
||||
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
|
||||
class ExtensionMethods {
|
||||
@Test
|
||||
fun simpleExtensionMethod() {
|
||||
fun String.escapeForXml() : String {
|
||||
return this
|
||||
.replace("&", "&")
|
||||
.replace("<", "<")
|
||||
.replace(">", ">")
|
||||
}
|
||||
|
||||
Assert.assertEquals("Nothing", "Nothing".escapeForXml())
|
||||
Assert.assertEquals("<Tag>", "<Tag>".escapeForXml())
|
||||
Assert.assertEquals("a&b", "a&b".escapeForXml())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun genericExtensionMethod() {
|
||||
fun <T> T.concatAsString(b: T) : String {
|
||||
return this.toString() + b.toString()
|
||||
}
|
||||
|
||||
Assert.assertEquals("12", "1".concatAsString("2"))
|
||||
Assert.assertEquals("12", 1.concatAsString(2))
|
||||
// This doesn't compile
|
||||
// Assert.assertEquals("12", 1.concatAsString(2.0))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun infixExtensionMethod() {
|
||||
infix fun Number.toPowerOf(exponent: Number): Double {
|
||||
return Math.pow(this.toDouble(), exponent.toDouble())
|
||||
}
|
||||
|
||||
Assert.assertEquals(9.0, 3 toPowerOf 2, 0.1)
|
||||
Assert.assertEquals(3.0, 9 toPowerOf 0.5, 0.1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun operatorExtensionMethod() {
|
||||
operator fun List<Int>.times(by: Int): List<Int> {
|
||||
return this.map { it * by }
|
||||
}
|
||||
|
||||
Assert.assertEquals(listOf(2, 4, 6), listOf(1, 2, 3) * 2)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.1'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'com.github.johnrengelman.shadow'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
jar {
|
||||
manifest {
|
||||
attributes "Main-Class": "com.baeldung.fatjar.Application"
|
||||
}
|
||||
|
||||
from {
|
||||
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
task customFatJar(type: Jar) {
|
||||
manifest {
|
||||
attributes 'Main-Class': 'com.baeldung.fatjar.Application'
|
||||
}
|
||||
baseName = 'all-in-one-jar'
|
||||
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
|
||||
with jar
|
||||
}
|
||||
|
||||
|
||||
dependencies{
|
||||
compile group: 'org.slf4j', name: 'slf4j-api', version: '1.7.25'
|
||||
compile group: 'org.slf4j', name: 'slf4j-simple', version: '1.7.25'
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package com.baeldung.fatjar;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
public class Application {
|
||||
|
||||
static final Logger logger = LoggerFactory.getLogger(Application.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
logger.info("Hello at Baeldung!");
|
||||
}
|
||||
|
||||
}
|
|
@ -14,7 +14,9 @@ import org.junit.Test;
|
|||
import javax.persistence.Query;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class HibernateSpatialTest {
|
||||
|
@ -76,10 +78,8 @@ public class HibernateSpatialTest {
|
|||
Query query = session.createQuery("select p from PointEntity p where within(p.point, :area) = true",
|
||||
PointEntity.class);
|
||||
query.setParameter("area", wktToGeometry("POLYGON((0 0, 0 5, 5 5, 5 0, 0 0))"));
|
||||
assertEquals(3, query.getResultList().size());
|
||||
assertEquals("POINT (1 1)", ((PointEntity) query.getResultList().get(0)).getPoint().toString());
|
||||
assertEquals("POINT (1 2)", ((PointEntity) query.getResultList().get(1)).getPoint().toString());
|
||||
assertEquals("POINT (3 4)", ((PointEntity) query.getResultList().get(2)).getPoint().toString());
|
||||
assertThat(query.getResultList().stream().map(p -> ((PointEntity) p).getPoint().toString()))
|
||||
.containsOnly("POINT (1 1)", "POINT (1 2)", "POINT (3 4)");
|
||||
}
|
||||
|
||||
private void insertPoint(String point) throws ParseException {
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
|
||||
http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<name>logback</name>
|
||||
<artifactId>logback</artifactId>
|
||||
<version>0.1-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<logback.version>1.2.3</logback.version>
|
||||
</properties>
|
||||
|
||||
<parent>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>parent-modules</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<relativePath>../../</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>${logback.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
|
@ -0,0 +1,14 @@
|
|||
package com.baeldung.logback;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class Example {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(Example.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
logger.info("Example log from {}", Example.class.getSimpleName());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package com.baeldung.logback;
|
||||
|
||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||
import ch.qos.logback.core.AppenderBase;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class MapAppender extends AppenderBase<ILoggingEvent> {
|
||||
|
||||
private final Map<String, ILoggingEvent> eventMap = new HashMap<>();
|
||||
|
||||
private String prefix;
|
||||
|
||||
@Override
|
||||
protected void append(final ILoggingEvent event) {
|
||||
if (prefix == null || "".equals(prefix)) {
|
||||
addError("Prefix is not set for MapAppender.");
|
||||
return;
|
||||
}
|
||||
|
||||
eventMap.put(prefix + System.currentTimeMillis(), event);
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
public void setPrefix(final String prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
public Map<String, ILoggingEvent> getEventMap() {
|
||||
return eventMap;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<configuration>
|
||||
|
||||
<appender name="map" class="com.baeldung.logback.MapAppender">
|
||||
<prefix>test</prefix>
|
||||
</appender>
|
||||
|
||||
<appender name="out" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="info">
|
||||
<appender-ref ref="map"/>
|
||||
<appender-ref ref="out"/>
|
||||
</root>
|
||||
|
||||
</configuration>
|
|
@ -0,0 +1,33 @@
|
|||
package com.baeldung.logback;
|
||||
|
||||
import ch.qos.logback.classic.Logger;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class MapAppenderIntegrationTest {
|
||||
|
||||
private Logger rootLogger;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
rootLogger = (Logger) LoggerFactory.getLogger("ROOT");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenLoggerEmitsLoggingEvent_thenAppenderReceivesEvent() throws Exception {
|
||||
rootLogger.info("Test from {}", this.getClass().getSimpleName());
|
||||
MapAppender appender = (MapAppender) rootLogger.getAppender("map");
|
||||
assertEquals(appender.getEventMap().size(), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenNoPrefixSet_whenLoggerEmitsEvent_thenAppenderReceivesNoEvent() throws Exception {
|
||||
rootLogger.info("Test from {}", this.getClass().getSimpleName());
|
||||
MapAppender appender = (MapAppender) rootLogger.getAppender("badMap");
|
||||
assertEquals(appender.getEventMap().size(), 0);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package com.baeldung.logback;
|
||||
|
||||
import ch.qos.logback.classic.Level;
|
||||
import ch.qos.logback.classic.LoggerContext;
|
||||
import ch.qos.logback.classic.spi.LoggingEvent;
|
||||
import ch.qos.logback.core.BasicStatusManager;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class MapAppenderTest {
|
||||
|
||||
private LoggerContext ctx;
|
||||
|
||||
private MapAppender mapAppender = new MapAppender();
|
||||
|
||||
private LoggingEvent event;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
ctx = new LoggerContext();
|
||||
ctx.setName("test context");
|
||||
ctx.setStatusManager(new BasicStatusManager());
|
||||
mapAppender.setContext(ctx);
|
||||
mapAppender.setPrefix("prefix");
|
||||
event = new LoggingEvent("fqcn", ctx.getLogger("logger"), Level.INFO, "Test message for logback appender", null, new Object[0]);
|
||||
ctx.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
ctx.stop();
|
||||
mapAppender.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenPrefixIsNull_thenMapAppenderDoesNotLog() throws Exception {
|
||||
mapAppender.setPrefix(null);
|
||||
mapAppender.append(event);
|
||||
assertTrue(mapAppender.getEventMap().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenPrefixIsEmpty_thenMapAppenderDoesNotLog() throws Exception {
|
||||
mapAppender.setPrefix("");
|
||||
mapAppender.append(event);
|
||||
assertTrue(mapAppender.getEventMap().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenLogMessageIsEmitted_thenMapAppenderReceivesMessage() throws Exception {
|
||||
mapAppender.append(event);
|
||||
assertEquals(mapAppender.getEventMap().size(), 1);
|
||||
mapAppender.getEventMap().forEach((k, v) -> assertTrue(k.startsWith("prefix")));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<configuration debug="true">
|
||||
|
||||
<appender name="map" class="com.baeldung.logback.MapAppender">
|
||||
<prefix>test</prefix>
|
||||
</appender>
|
||||
|
||||
<appender name="badMap" class="com.baeldung.logback.MapAppender"/>
|
||||
|
||||
<root level="debug">
|
||||
<appender-ref ref="map"/>
|
||||
<appender-ref ref="badMap"/>
|
||||
</root>
|
||||
|
||||
</configuration>
|
2
pom.xml
2
pom.xml
|
@ -107,6 +107,7 @@
|
|||
<module>logging-modules/log-mdc</module>
|
||||
<module>logging-modules/log4j</module>
|
||||
<module>logging-modules/log4j2</module>
|
||||
<module>logging-modules/logback</module>
|
||||
<module>lombok</module>
|
||||
<!-- <module>kotlin</module>-->
|
||||
<module>mapstruct</module>
|
||||
|
@ -201,6 +202,7 @@
|
|||
<module>spring-rest-query-language</module>
|
||||
<module>spring-rest</module>
|
||||
<module>spring-rest-simple</module>
|
||||
<module>spring-security-acl</module>
|
||||
<module>spring-security-cache-control</module>
|
||||
<module>spring-security-client/spring-security-jsp-authentication</module>
|
||||
<module>spring-security-client/spring-security-jsp-authorize</module>
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.0.0.M6</version>
|
||||
<version>2.0.0.M7</version>
|
||||
<relativePath /> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
- [Intro to Spring Cloud Netflix - Hystrix](http://www.baeldung.com/spring-cloud-netflix-hystrix)
|
||||
- [Dockerizing a Spring Boot Application](http://www.baeldung.com/dockerizing-spring-boot-application)
|
||||
- [Introduction to Spring Cloud Rest Client with Netflix Ribbon](http://www.baeldung.com/spring-cloud-rest-client-with-netflix-ribbon)
|
||||
- [A Quick Guide to Spring Cloud Consul](http://www.baeldung.com/spring-cloud-consul)
|
||||
|
||||
### Relevant Articles:
|
||||
- [Introduction to Spring Cloud Rest Client with Netflix Ribbon](http://www.baeldung.com/spring-cloud-rest-client-with-netflix-ribbon)
|
||||
|
|
|
@ -1,59 +1,16 @@
|
|||
package com.baeldung.spring.cloud.consul.discovery;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
@RestController
|
||||
public class DiscoveryClientApplication {
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private DiscoveryClient discoveryClient;
|
||||
|
||||
@GetMapping("/discoveryClient")
|
||||
public String home() {
|
||||
return restTemplate().getForEntity(serviceUrl().resolve("/ping"), String.class)
|
||||
.getBody();
|
||||
}
|
||||
|
||||
@GetMapping("/ping")
|
||||
public String ping() {
|
||||
return "pong";
|
||||
}
|
||||
|
||||
@RequestMapping("/my-health-check")
|
||||
public ResponseEntity<String> myCustomCheck() {
|
||||
return new ResponseEntity<>(HttpStatus.OK);
|
||||
}
|
||||
|
||||
public URI serviceUrl() {
|
||||
return discoveryClient.getInstances("myApp")
|
||||
.stream()
|
||||
.findFirst()
|
||||
.map(si -> si.getUri())
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
new SpringApplicationBuilder(DiscoveryClientApplication.class).web(true)
|
||||
.run(args);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
package com.baeldung.spring.cloud.consul.discovery;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.naming.ServiceUnavailableException;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestClientException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@RestController
|
||||
public class DiscoveryClientController {
|
||||
|
||||
@Autowired
|
||||
private DiscoveryClient discoveryClient;
|
||||
|
||||
private final RestTemplate restTemplate = new RestTemplate();
|
||||
|
||||
@GetMapping("/discoveryClient")
|
||||
public String discoveryPing() throws RestClientException, ServiceUnavailableException {
|
||||
URI service = serviceUrl().map(s -> s.resolve("/ping"))
|
||||
.orElseThrow(ServiceUnavailableException::new);
|
||||
return restTemplate.getForEntity(service, String.class)
|
||||
.getBody();
|
||||
}
|
||||
|
||||
@GetMapping("/ping")
|
||||
public String ping() {
|
||||
return "pong";
|
||||
}
|
||||
|
||||
@GetMapping("/my-health-check")
|
||||
public ResponseEntity<String> myCustomCheck() {
|
||||
return new ResponseEntity<>(HttpStatus.OK);
|
||||
}
|
||||
|
||||
public Optional<URI> serviceUrl() {
|
||||
return discoveryClient.getInstances("myApp")
|
||||
.stream()
|
||||
.findFirst()
|
||||
.map(si -> si.getUri());
|
||||
}
|
||||
|
||||
}
|
|
@ -2,24 +2,9 @@ package com.baeldung.spring.cloud.consul.health;
|
|||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@SpringBootApplication
|
||||
@RestController
|
||||
public class ServiceDiscoveryApplication {
|
||||
@RequestMapping("/my-health-check")
|
||||
public ResponseEntity<String> myCustomCheck() {
|
||||
String message = "Testing my healh check function";
|
||||
return new ResponseEntity<>(message, HttpStatus.FORBIDDEN);
|
||||
}
|
||||
|
||||
@RequestMapping("/ping")
|
||||
public String ping() {
|
||||
return "pong";
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
new SpringApplicationBuilder(ServiceDiscoveryApplication.class).web(true)
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
package com.baeldung.spring.cloud.consul.health;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class ServiceDiscoveryController {
|
||||
|
||||
@GetMapping("/ping")
|
||||
public String ping() {
|
||||
return "pong";
|
||||
}
|
||||
|
||||
@GetMapping("/my-health-check")
|
||||
public ResponseEntity<String> myCustomCheck() {
|
||||
String message = "Testing my healh check function";
|
||||
return new ResponseEntity<>(message, HttpStatus.FORBIDDEN);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,34 +1,16 @@
|
|||
package com.baeldung.spring.cloud.consul.properties;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@SpringBootApplication
|
||||
@RestController
|
||||
public class DistributedPropertiesApplication {
|
||||
|
||||
@Value("${my.prop}")
|
||||
String value;
|
||||
|
||||
@Autowired
|
||||
private MyProperties properties;
|
||||
|
||||
@RequestMapping("/getConfigFromValue")
|
||||
public String getConfigFromValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@RequestMapping("/getConfigFromProperty")
|
||||
public String getConfigFromProperty() {
|
||||
return properties.getProp();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
new SpringApplicationBuilder(DistributedPropertiesApplication.class).web(true)
|
||||
.run(args);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
package com.baeldung.spring.cloud.consul.properties;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class DistributedPropertiesController {
|
||||
|
||||
@Value("${my.prop}")
|
||||
String value;
|
||||
|
||||
@Autowired
|
||||
private MyProperties properties;
|
||||
|
||||
@GetMapping("/getConfigFromValue")
|
||||
public String getConfigFromValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@GetMapping("/getConfigFromProperty")
|
||||
public String getConfigFromProperty() {
|
||||
return properties.getProp();
|
||||
}
|
||||
|
||||
}
|
|
@ -8,6 +8,7 @@ import org.springframework.context.annotation.Configuration;
|
|||
@Configuration
|
||||
@ConfigurationProperties("my")
|
||||
public class MyProperties {
|
||||
|
||||
private String prop;
|
||||
|
||||
public String getProp() {
|
||||
|
@ -17,4 +18,5 @@ public class MyProperties {
|
|||
public void setProp(String prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
package com.baeldung.spring.cloud.consul.ribbon;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@SpringBootApplication
|
||||
@RestController
|
||||
public class RibbonClientApplication {
|
||||
|
||||
@LoadBalanced
|
||||
@Bean
|
||||
RestTemplate getRestTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
|
||||
@Autowired
|
||||
RestTemplate restTemplate;
|
||||
|
||||
@RequestMapping("/ribbonClient")
|
||||
public String home() {
|
||||
return restTemplate.getForObject("http://myApp/ping", String.class);
|
||||
}
|
||||
|
||||
@GetMapping("/ping")
|
||||
public String ping() {
|
||||
return "pong";
|
||||
}
|
||||
|
||||
@RequestMapping("/my-health-check")
|
||||
public ResponseEntity<String> myCustomCheck() {
|
||||
return new ResponseEntity<>(HttpStatus.OK);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
new SpringApplicationBuilder(RibbonClientApplication.class).web(true)
|
||||
.run(args);
|
||||
}
|
||||
}
|
|
@ -22,7 +22,7 @@
|
|||
<spring-social.version>1.1.4.RELEASE</spring-social.version>
|
||||
<javax-mail.version>1.4.7</javax-mail.version>
|
||||
<javax-activation.version>1.1.1</javax-activation.version>
|
||||
|
||||
<junit.version>4.12</junit.version>
|
||||
<maven-eclipse-plugin.version>2.10</maven-eclipse-plugin.version>
|
||||
<exec-maven-plugin.version>1.5.0</exec-maven-plugin.version>
|
||||
</properties>
|
||||
|
@ -56,7 +56,7 @@
|
|||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<version>${exec-maven-plugin.version}</version>
|
||||
<configuration>
|
||||
<mainClass>com.baeldung.samples.Main</mainClass>
|
||||
<mainClass>com.baeldung.samples.FileCopyConfig</mainClass>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
@ -106,6 +106,12 @@
|
|||
<artifactId>spring-integration-file</artifactId>
|
||||
<version>${spring.integration.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
## Spring Security ACL Project
|
||||
|
||||
### Relevant Articles
|
||||
- [Introduction to Spring Security ACL](http://www.baeldung.com/spring-security-acl)
|
|
@ -0,0 +1,66 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>spring-security-acl</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<packaging>war</packaging>
|
||||
|
||||
<name>spring-security-acl</name>
|
||||
<description>Spring Security ACL</description>
|
||||
|
||||
<parent>
|
||||
<artifactId>parent-boot-5</artifactId>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<relativePath>../parent-boot-5</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-acl</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-config</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context-support</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.ehcache</groupId>
|
||||
<artifactId>ehcache-core</artifactId>
|
||||
<version>2.6.11</version>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,80 @@
|
|||
package org.baeldung.acl.config;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.cache.ehcache.EhCacheFactoryBean;
|
||||
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
|
||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
|
||||
import org.springframework.security.acls.AclPermissionCacheOptimizer;
|
||||
import org.springframework.security.acls.AclPermissionEvaluator;
|
||||
import org.springframework.security.acls.domain.AclAuthorizationStrategy;
|
||||
import org.springframework.security.acls.domain.AclAuthorizationStrategyImpl;
|
||||
import org.springframework.security.acls.domain.ConsoleAuditLogger;
|
||||
import org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy;
|
||||
import org.springframework.security.acls.domain.EhCacheBasedAclCache;
|
||||
import org.springframework.security.acls.jdbc.BasicLookupStrategy;
|
||||
import org.springframework.security.acls.jdbc.JdbcMutableAclService;
|
||||
import org.springframework.security.acls.jdbc.LookupStrategy;
|
||||
import org.springframework.security.acls.model.PermissionGrantingStrategy;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
public class ACLContext {
|
||||
|
||||
@Autowired
|
||||
DataSource dataSource;
|
||||
|
||||
@Bean
|
||||
public EhCacheBasedAclCache aclCache() {
|
||||
return new EhCacheBasedAclCache(aclEhCacheFactoryBean().getObject(), permissionGrantingStrategy(), aclAuthorizationStrategy());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EhCacheFactoryBean aclEhCacheFactoryBean() {
|
||||
EhCacheFactoryBean ehCacheFactoryBean = new EhCacheFactoryBean();
|
||||
ehCacheFactoryBean.setCacheManager(aclCacheManager().getObject());
|
||||
ehCacheFactoryBean.setCacheName("aclCache");
|
||||
return ehCacheFactoryBean;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EhCacheManagerFactoryBean aclCacheManager() {
|
||||
return new EhCacheManagerFactoryBean();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PermissionGrantingStrategy permissionGrantingStrategy() {
|
||||
return new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AclAuthorizationStrategy aclAuthorizationStrategy() {
|
||||
return new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_ADMIN"));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler() {
|
||||
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
|
||||
AclPermissionEvaluator permissionEvaluator = new AclPermissionEvaluator(aclService());
|
||||
expressionHandler.setPermissionEvaluator(permissionEvaluator);
|
||||
expressionHandler.setPermissionCacheOptimizer(new AclPermissionCacheOptimizer(aclService()));
|
||||
return expressionHandler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public LookupStrategy lookupStrategy() {
|
||||
return new BasicLookupStrategy(dataSource, aclCache(), aclAuthorizationStrategy(), new ConsoleAuditLogger());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JdbcMutableAclService aclService() {
|
||||
return new JdbcMutableAclService(dataSource, lookupStrategy(), aclCache());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package org.baeldung.acl.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
|
||||
public class AclMethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {
|
||||
|
||||
@Autowired
|
||||
MethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler;
|
||||
|
||||
@Override
|
||||
protected MethodSecurityExpressionHandler createExpressionHandler() {
|
||||
return defaultMethodSecurityExpressionHandler;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package org.baeldung.acl.config;
|
||||
|
||||
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
@Configuration
|
||||
@EnableTransactionManagement
|
||||
@EnableJpaRepositories(basePackages = "org.baeldung.acl.persistence.dao")
|
||||
@PropertySource("classpath:org.baeldung.acl.datasource.properties")
|
||||
@EntityScan(basePackages={ "org.baeldung.acl.persistence.entity" })
|
||||
public class JPAPersistenceConfig {
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package org.baeldung.acl.persistence.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.baeldung.acl.persistence.entity.NoticeMessage;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.security.access.prepost.PostAuthorize;
|
||||
import org.springframework.security.access.prepost.PostFilter;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
|
||||
public interface NoticeMessageRepository extends JpaRepository<NoticeMessage, Long>{
|
||||
|
||||
@PostFilter("hasPermission(filterObject, 'READ')")
|
||||
List<NoticeMessage> findAll();
|
||||
|
||||
@PostAuthorize("hasPermission(returnObject, 'READ')")
|
||||
NoticeMessage findById(Integer id);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@PreAuthorize("hasPermission(#noticeMessage, 'WRITE')")
|
||||
NoticeMessage save(@Param("noticeMessage")NoticeMessage noticeMessage);
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package org.baeldung.acl.persistence.entity;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name="system_message")
|
||||
public class NoticeMessage {
|
||||
|
||||
@Id
|
||||
@Column
|
||||
private Integer id;
|
||||
@Column
|
||||
private String content;
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
INSERT INTO acl_sid (id, principal, sid) VALUES
|
||||
(1, 1, 'manager'),
|
||||
(2, 1, 'hr'),
|
||||
(3, 0, 'ROLE_EDITOR');
|
||||
|
||||
INSERT INTO acl_class (id, class) VALUES
|
||||
(1, 'org.baeldung.acl.persistence.entity.NoticeMessage');
|
||||
|
||||
INSERT INTO system_message(id,content) VALUES
|
||||
(1,'First Level Message'),
|
||||
(2,'Second Level Message'),
|
||||
(3,'Third Level Message');
|
||||
|
||||
INSERT INTO acl_object_identity (id, object_id_class, object_id_identity, parent_object, owner_sid, entries_inheriting) VALUES
|
||||
(1, 1, 1, NULL, 3, 0),
|
||||
(2, 1, 2, NULL, 3, 0),
|
||||
(3, 1, 3, NULL, 3, 0);
|
||||
|
||||
INSERT INTO acl_entry (id, acl_object_identity, ace_order, sid, mask, granting, audit_success, audit_failure) VALUES
|
||||
(1, 1, 1, 1, 1, 1, 1, 1),
|
||||
(2, 1, 2, 1, 2, 1, 1, 1),
|
||||
(3, 1, 3, 3, 1, 1, 1, 1),
|
||||
(4, 2, 1, 2, 1, 1, 1, 1),
|
||||
(5, 2, 2, 3, 1, 1, 1, 1),
|
||||
(6, 3, 1, 3, 1, 1, 1, 1),
|
||||
(7, 3, 2, 3, 2, 1, 1, 1);
|
|
@ -0,0 +1,58 @@
|
|||
create table IF NOT EXISTS system_message (id integer not null, content varchar(255), primary key (id));
|
||||
|
||||
CREATE TABLE IF NOT EXISTS acl_sid (
|
||||
id bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
principal tinyint(1) NOT NULL,
|
||||
sid varchar(100) NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY unique_uk_1 (sid,principal)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS acl_class (
|
||||
id bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
class varchar(255) NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY unique_uk_2 (class)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS acl_entry (
|
||||
id bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
acl_object_identity bigint(20) NOT NULL,
|
||||
ace_order int(11) NOT NULL,
|
||||
sid bigint(20) NOT NULL,
|
||||
mask int(11) NOT NULL,
|
||||
granting tinyint(1) NOT NULL,
|
||||
audit_success tinyint(1) NOT NULL,
|
||||
audit_failure tinyint(1) NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY unique_uk_4 (acl_object_identity,ace_order)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS acl_object_identity (
|
||||
id bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
object_id_class bigint(20) NOT NULL,
|
||||
object_id_identity bigint(20) NOT NULL,
|
||||
parent_object bigint(20) DEFAULT NULL,
|
||||
owner_sid bigint(20) DEFAULT NULL,
|
||||
entries_inheriting tinyint(1) NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY unique_uk_3 (object_id_class,object_id_identity)
|
||||
);
|
||||
|
||||
ALTER TABLE acl_entry
|
||||
ADD FOREIGN KEY (acl_object_identity) REFERENCES acl_object_identity(id);
|
||||
|
||||
ALTER TABLE acl_entry
|
||||
ADD FOREIGN KEY (sid) REFERENCES acl_sid(id);
|
||||
|
||||
--
|
||||
-- Constraints for table acl_object_identity
|
||||
--
|
||||
ALTER TABLE acl_object_identity
|
||||
ADD FOREIGN KEY (parent_object) REFERENCES acl_object_identity (id);
|
||||
|
||||
ALTER TABLE acl_object_identity
|
||||
ADD FOREIGN KEY (object_id_class) REFERENCES acl_class (id);
|
||||
|
||||
ALTER TABLE acl_object_identity
|
||||
ADD FOREIGN KEY (owner_sid) REFERENCES acl_sid (id);
|
|
@ -0,0 +1,12 @@
|
|||
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE
|
||||
spring.datasource.username=sa
|
||||
spring.datasource.password=
|
||||
spring.datasource.driverClassName=org.h2.Driver
|
||||
spring.jpa.hibernate.ddl-auto=update
|
||||
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
|
||||
|
||||
spring.h2.console.path=/myconsole
|
||||
spring.h2.console.enabled=true
|
||||
spring.datasource.initialize=true
|
||||
spring.datasource.schema=classpath:acl-schema.sql
|
||||
spring.datasource.data=classpath:acl-data.sql
|
|
@ -0,0 +1,119 @@
|
|||
package org.baeldung.acl;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.baeldung.acl.persistence.dao.NoticeMessageRepository;
|
||||
import org.baeldung.acl.persistence.entity.NoticeMessage;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.test.context.support.WithMockUser;
|
||||
import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
|
||||
import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
|
||||
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
|
||||
import org.springframework.test.context.web.ServletTestExecutionListener;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
@TestExecutionListeners(listeners={ServletTestExecutionListener.class,
|
||||
DependencyInjectionTestExecutionListener.class,
|
||||
DirtiesContextTestExecutionListener.class,
|
||||
TransactionalTestExecutionListener.class,
|
||||
WithSecurityContextTestExecutionListener.class})
|
||||
public class SpringAclTest extends AbstractJUnit4SpringContextTests{
|
||||
|
||||
private static Integer FIRST_MESSAGE_ID = 1;
|
||||
private static Integer SECOND_MESSAGE_ID = 2;
|
||||
private static Integer THIRD_MESSAGE_ID = 3;
|
||||
private static String EDITTED_CONTENT = "EDITED";
|
||||
|
||||
@Configuration
|
||||
@ComponentScan("org.baeldung.acl.*")
|
||||
public static class SpringConfig {
|
||||
|
||||
}
|
||||
|
||||
@Autowired
|
||||
NoticeMessageRepository repo;
|
||||
|
||||
@Test
|
||||
@WithMockUser(username="manager")
|
||||
public void givenUserManager_whenFindAllMessage_thenReturnFirstMessage(){
|
||||
List<NoticeMessage> details = repo.findAll();
|
||||
assertNotNull(details);
|
||||
assertEquals(1,details.size());
|
||||
assertEquals(FIRST_MESSAGE_ID,details.get(0).getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(username="manager")
|
||||
public void givenUserManager_whenFind1stMessageByIdAndUpdateItsContent_thenOK(){
|
||||
NoticeMessage firstMessage = repo.findById(FIRST_MESSAGE_ID);
|
||||
assertNotNull(firstMessage);
|
||||
assertEquals(FIRST_MESSAGE_ID,firstMessage.getId());
|
||||
|
||||
firstMessage.setContent(EDITTED_CONTENT);
|
||||
repo.save(firstMessage);
|
||||
|
||||
NoticeMessage editedFirstMessage = repo.findById(FIRST_MESSAGE_ID);
|
||||
assertNotNull(editedFirstMessage);
|
||||
assertEquals(FIRST_MESSAGE_ID,editedFirstMessage.getId());
|
||||
assertEquals(EDITTED_CONTENT,editedFirstMessage.getContent());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(username="hr")
|
||||
public void givenUsernameHr_whenFindMessageById2_thenOK(){
|
||||
NoticeMessage secondMessage = repo.findById(SECOND_MESSAGE_ID);
|
||||
assertNotNull(secondMessage);
|
||||
assertEquals(SECOND_MESSAGE_ID,secondMessage.getId());
|
||||
}
|
||||
|
||||
@Test(expected=AccessDeniedException.class)
|
||||
@WithMockUser(username="hr")
|
||||
public void givenUsernameHr_whenUpdateMessageWithId2_thenFail(){
|
||||
NoticeMessage secondMessage = new NoticeMessage();
|
||||
secondMessage.setId(SECOND_MESSAGE_ID);
|
||||
secondMessage.setContent(EDITTED_CONTENT);
|
||||
repo.save(secondMessage);
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(roles={"EDITOR"})
|
||||
public void givenRoleEditor_whenFindAllMessage_thenReturn3Message(){
|
||||
List<NoticeMessage> details = repo.findAll();
|
||||
assertNotNull(details);
|
||||
assertEquals(3,details.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(roles={"EDITOR"})
|
||||
public void givenRoleEditor_whenUpdateThirdMessage_thenOK(){
|
||||
NoticeMessage thirdMessage = new NoticeMessage();
|
||||
thirdMessage.setId(THIRD_MESSAGE_ID);
|
||||
thirdMessage.setContent(EDITTED_CONTENT);
|
||||
repo.save(thirdMessage);
|
||||
}
|
||||
|
||||
@Test(expected=AccessDeniedException.class)
|
||||
@WithMockUser(roles={"EDITOR"})
|
||||
public void givenRoleEditor_whenFind1stMessageByIdAndUpdateContent_thenFail(){
|
||||
NoticeMessage firstMessage = repo.findById(FIRST_MESSAGE_ID);
|
||||
assertNotNull(firstMessage);
|
||||
assertEquals(FIRST_MESSAGE_ID,firstMessage.getId());
|
||||
firstMessage.setContent(EDITTED_CONTENT);
|
||||
repo.save(firstMessage);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package com.baeldung.junit5.bean;
|
||||
|
||||
/**
|
||||
* Bean that contains utility methods to work with numbers.
|
||||
*
|
||||
* @author Donato Rimenti
|
||||
*
|
||||
*/
|
||||
public class NumbersBean {
|
||||
|
||||
/**
|
||||
* Returns true if a number is even, false otherwise.
|
||||
*
|
||||
* @param number
|
||||
* the number to check
|
||||
* @return true if the argument is even, false otherwise
|
||||
*/
|
||||
public boolean isNumberEven(int number) {
|
||||
return number % 2 == 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package com.baeldung.junit5.bean.test;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.baeldung.junit5.bean.NumbersBean;
|
||||
|
||||
/**
|
||||
* Test class for {@link NumbersBean}.
|
||||
*
|
||||
* @author Donato Rimenti
|
||||
*
|
||||
*/
|
||||
public class NumbersBeanUnitTest {
|
||||
|
||||
/**
|
||||
* The bean to test.
|
||||
*/
|
||||
private NumbersBean bean = new NumbersBean();
|
||||
|
||||
/**
|
||||
* Tests that when an even number is passed to
|
||||
* {@link NumbersBean#isNumberEven(int)}, true is returned.
|
||||
*/
|
||||
@Test
|
||||
void givenEvenNumber_whenCheckingIsNumberEven_thenTrue() {
|
||||
boolean result = bean.isNumberEven(8);
|
||||
|
||||
Assertions.assertTrue(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that when an odd number is passed to
|
||||
* {@link NumbersBean#isNumberEven(int)}, false is returned.
|
||||
*/
|
||||
@Test
|
||||
void givenOddNumber_whenCheckingIsNumberEven_thenFalse() {
|
||||
boolean result = bean.isNumberEven(3);
|
||||
|
||||
Assertions.assertFalse(result);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
package org.baeldung.bddmockito;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.BDDMockito.*;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
|
||||
|
||||
public class BDDMockitoTest {
|
||||
|
||||
PhoneBookService phoneBookService;
|
||||
PhoneBookRepository phoneBookRepository;
|
||||
|
||||
String momContactName = "Mom";
|
||||
String momPhoneNumber = "01234";
|
||||
String xContactName = "x";
|
||||
String tooLongPhoneNumber = "01111111111111";
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
phoneBookRepository = Mockito.mock(PhoneBookRepository.class);
|
||||
phoneBookService = new PhoneBookService(phoneBookRepository);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenValidContactName_whenSearchInPhoneBook_thenRetunPhoneNumber() {
|
||||
given(phoneBookRepository.contains(momContactName)).willReturn(true);
|
||||
given(phoneBookRepository.getPhoneNumberByContactName(momContactName))
|
||||
.will((InvocationOnMock invocation) -> {
|
||||
if(invocation.getArgument(0).equals(momContactName)) {
|
||||
return momPhoneNumber;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
String phoneNumber = phoneBookService.search(momContactName);
|
||||
|
||||
then(phoneBookRepository).should().contains(momContactName);
|
||||
then(phoneBookRepository).should().getPhoneNumberByContactName(momContactName);
|
||||
Assert.assertEquals(phoneNumber, momPhoneNumber);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenInvalidContactName_whenSearch_thenRetunNull() {
|
||||
given(phoneBookRepository.contains(xContactName)).willReturn(false);
|
||||
|
||||
String phoneNumber = phoneBookService.search(xContactName);
|
||||
|
||||
then(phoneBookRepository).should().contains(xContactName);
|
||||
then(phoneBookRepository).should(never()).getPhoneNumberByContactName(xContactName);
|
||||
Assert.assertEquals(phoneNumber, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenValidContactNameAndPhoneNumber_whenRegister_thenSucceed() {
|
||||
given(phoneBookRepository.contains(momContactName)).willReturn(false);
|
||||
|
||||
phoneBookService.register(momContactName, momPhoneNumber);
|
||||
|
||||
verify(phoneBookRepository).insert(momContactName, momPhoneNumber);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenEmptyPhoneNumber_whenRegister_thenFail() {
|
||||
given(phoneBookRepository.contains(momContactName)).willReturn(false);
|
||||
|
||||
phoneBookService.register(xContactName, "");
|
||||
|
||||
then(phoneBookRepository).should(never()).insert(momContactName, momPhoneNumber);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenLongPhoneNumber_whenRegister_thenFail() {
|
||||
given(phoneBookRepository.contains(xContactName)).willReturn(false);
|
||||
willThrow(new RuntimeException())
|
||||
.given(phoneBookRepository).insert(any(String.class), eq(tooLongPhoneNumber));
|
||||
|
||||
try {
|
||||
phoneBookService.register(xContactName, tooLongPhoneNumber);
|
||||
fail("Should throw exception");
|
||||
} catch (RuntimeException ex) { }
|
||||
|
||||
then(phoneBookRepository).should(never()).insert(momContactName, tooLongPhoneNumber);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenExistentContactName_whenRegister_thenFail() {
|
||||
given(phoneBookRepository.contains(momContactName))
|
||||
.willThrow(new RuntimeException("Name already exist"));
|
||||
|
||||
try {
|
||||
phoneBookService.register(momContactName, momPhoneNumber);
|
||||
fail("Should throw exception");
|
||||
} catch(Exception ex) { }
|
||||
|
||||
then(phoneBookRepository).should(never()).insert(momContactName, momPhoneNumber);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package org.baeldung.bddmockito;
|
||||
|
||||
public interface PhoneBookRepository {
|
||||
|
||||
/**
|
||||
* Insert phone record
|
||||
* @param name Contact name
|
||||
* @param phone Phone number
|
||||
*/
|
||||
void insert(String name, String phone);
|
||||
|
||||
/**
|
||||
* Search for contact phone number
|
||||
* @param name Contact name
|
||||
* @return phone number
|
||||
*/
|
||||
String getPhoneNumberByContactName(String name);
|
||||
|
||||
/**
|
||||
* Check if the phonebook contains this contact
|
||||
* @param name Contact name
|
||||
* @return true if this contact name exists
|
||||
*/
|
||||
boolean contains(String name);
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package org.baeldung.bddmockito;
|
||||
|
||||
public class PhoneBookService {
|
||||
|
||||
private PhoneBookRepository phoneBookRepository;
|
||||
|
||||
public PhoneBookService(PhoneBookRepository phoneBookRepository) {
|
||||
this.phoneBookRepository = phoneBookRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a contact
|
||||
* @param name Contact name
|
||||
* @param phone Phone number
|
||||
*/
|
||||
public void register(String name, String phone) {
|
||||
if(!name.isEmpty() && !phone.isEmpty() && !phoneBookRepository.contains(name)) {
|
||||
phoneBookRepository.insert(name, phone);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for a phone number by contact name
|
||||
* @param name Contact name
|
||||
* @return Phone number
|
||||
*/
|
||||
public String search(String name) {
|
||||
if(!name.isEmpty() && phoneBookRepository.contains(name)) {
|
||||
return phoneBookRepository.getPhoneNumberByContactName(name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package com.baeldung.vavr.future;
|
||||
|
||||
public class Util {
|
||||
|
||||
public static String appendData(String initial) {
|
||||
return initial + "Baeldung!";
|
||||
}
|
||||
|
||||
public static int divideByZero(int num) {
|
||||
return num / 0;
|
||||
}
|
||||
|
||||
public static String getSubstringMinusOne(String s) {
|
||||
return s.substring(-1);
|
||||
}
|
||||
|
||||
public static String getSubstringMinusTwo(String s) {
|
||||
return s.substring(-2);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
package com.baeldung.vavr.future;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import io.vavr.Tuple;
|
||||
import io.vavr.Tuple2;
|
||||
import io.vavr.concurrent.Future;
|
||||
import io.vavr.control.Option;
|
||||
import io.vavr.control.Try;
|
||||
|
||||
public class FutureTest {
|
||||
|
||||
@Test
|
||||
public void whenChangeExecutorService_thenCorrect() {
|
||||
String initialValue = "Welcome to ";
|
||||
Future<String> resultFuture = Future.of(
|
||||
Executors.newSingleThreadExecutor(),
|
||||
() -> Util.appendData(initialValue));
|
||||
String result = resultFuture.get();
|
||||
|
||||
assertThat(result).isEqualTo("Welcome to Baeldung!");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenAppendData_thenCorrect1() {
|
||||
String initialValue = "Welcome to ";
|
||||
Future<String> resultFuture = Future.of(() -> Util.appendData(initialValue));
|
||||
String result = resultFuture.get();
|
||||
|
||||
assertThat(result).isEqualTo("Welcome to Baeldung!");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenAppendData_thenCorrect2() {
|
||||
String initialValue = "Welcome to ";
|
||||
Future<String> resultFuture = Future.of(() -> Util.appendData(initialValue));
|
||||
resultFuture.await();
|
||||
Option<Try<String>> futureOption = resultFuture.getValue();
|
||||
Try<String> futureTry = futureOption.get();
|
||||
String result = futureTry.get();
|
||||
|
||||
assertThat(result).isEqualTo("Welcome to Baeldung!");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenAppendData_thenSuccess() {
|
||||
String initialValue = "Welcome to ";
|
||||
Future<String> resultFuture = Future.of(() -> Util.appendData(initialValue))
|
||||
.onSuccess(finalResult -> System.out.println("Successfully Completed - Result: " + finalResult))
|
||||
.onFailure(finalResult -> System.out.println("Failed - Result: " + finalResult));
|
||||
String result = resultFuture.get();
|
||||
|
||||
assertThat(result).isEqualTo("Welcome to Baeldung!");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenChainingCallbacks_thenCorrect() {
|
||||
String initialValue = "Welcome to ";
|
||||
Future<String> resultFuture = Future.of(() -> Util.appendData(initialValue))
|
||||
.andThen(finalResult -> System.out.println("Completed - 1: " + finalResult))
|
||||
.andThen(finalResult -> System.out.println("Completed - 2: " + finalResult));
|
||||
String result = resultFuture.get();
|
||||
|
||||
assertThat(result).isEqualTo("Welcome to Baeldung!");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenCallAwait_thenCorrect() {
|
||||
String initialValue = "Welcome to ";
|
||||
Future<String> resultFuture = Future.of(() -> Util.appendData(initialValue));
|
||||
resultFuture = resultFuture.await();
|
||||
String result = resultFuture.get();
|
||||
|
||||
assertThat(result).isEqualTo("Welcome to Baeldung!");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenDivideByZero_thenGetThrowable1() {
|
||||
Future<Integer> resultFuture = Future.of(() -> Util.divideByZero(10));
|
||||
Future<Throwable> throwableFuture = resultFuture.failed();
|
||||
Throwable throwable = throwableFuture.get();
|
||||
|
||||
assertThat(throwable.getMessage()).isEqualTo("/ by zero");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenDivideByZero_thenGetThrowable2() {
|
||||
Future<Integer> resultFuture = Future.of(() -> Util.divideByZero(10));
|
||||
resultFuture.await();
|
||||
Option<Throwable> throwableOption = resultFuture.getCause();
|
||||
Throwable throwable = throwableOption.get();
|
||||
|
||||
assertThat(throwable.getMessage()).isEqualTo("/ by zero");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenDivideByZero_thenCorrect() throws InterruptedException {
|
||||
Future<Integer> resultFuture = Future.of(() -> Util.divideByZero(10));
|
||||
resultFuture.await();
|
||||
|
||||
assertThat(resultFuture.isCompleted()).isTrue();
|
||||
assertThat(resultFuture.isSuccess()).isFalse();
|
||||
assertThat(resultFuture.isFailure()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenAppendData_thenFutureNotEmpty() {
|
||||
String initialValue = "Welcome to ";
|
||||
Future<String> resultFuture = Future.of(() -> Util.appendData(initialValue));
|
||||
resultFuture.await();
|
||||
|
||||
assertThat(resultFuture.isEmpty()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenCallZip_thenCorrect() {
|
||||
Future<Tuple2<String, Integer>> future = Future.of(() -> "John")
|
||||
.zip(Future.of(() -> new Integer(5)));
|
||||
future.await();
|
||||
|
||||
assertThat(future.get()).isEqualTo(Tuple.of("John", new Integer(5)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenConvertToCompletableFuture_thenCorrect() throws InterruptedException, ExecutionException {
|
||||
String initialValue = "Welcome to ";
|
||||
Future<String> resultFuture = Future.of(() -> Util.appendData(initialValue));
|
||||
CompletableFuture<String> convertedFuture = resultFuture.toCompletableFuture();
|
||||
|
||||
assertThat(convertedFuture.get()).isEqualTo("Welcome to Baeldung!");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenCallMap_thenCorrect() {
|
||||
Future<String> futureResult = Future.of(() -> new StringBuilder("from Baeldung"))
|
||||
.map(a -> "Hello " + a);
|
||||
futureResult.await();
|
||||
|
||||
assertThat(futureResult.get()).isEqualTo("Hello from Baeldung");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenFutureFails_thenGetErrorMessage() {
|
||||
Future<String> resultFuture = Future.of(() -> Util.getSubstringMinusOne("Hello"));
|
||||
Future<String> errorMessageFuture = resultFuture.recover(Throwable::getMessage);
|
||||
String errorMessage = errorMessageFuture.get();
|
||||
|
||||
assertThat(errorMessage).isEqualTo("String index out of range: -1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenFutureFails_thenGetAnotherFuture() {
|
||||
Future<String> resultFuture = Future.of(() -> Util.getSubstringMinusOne("Hello"));
|
||||
Future<String> errorMessageFuture = resultFuture.recoverWith(a -> Future.of(a::getMessage));
|
||||
String errorMessage = errorMessageFuture.get();
|
||||
|
||||
assertThat(errorMessage).isEqualTo("String index out of range: -1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenBothFuturesFail_thenGetErrorMessage() {
|
||||
Future<String> future1 = Future.of(() -> Util.getSubstringMinusOne("Hello"));
|
||||
Future<String> future2 = Future.of(() -> Util.getSubstringMinusTwo("Hello"));
|
||||
Future<String> errorMessageFuture = future1.fallbackTo(future2);
|
||||
Future<Throwable> errorMessage = errorMessageFuture.failed();
|
||||
|
||||
assertThat(
|
||||
errorMessage.get().getMessage())
|
||||
.isEqualTo("String index out of range: -1");
|
||||
}
|
||||
}
|
|
@ -1,289 +0,0 @@
|
|||
package com.baeldung.vavr.future;
|
||||
|
||||
import static io.vavr.API.$;
|
||||
import static io.vavr.API.Case;
|
||||
import static io.vavr.API.Match;
|
||||
import static io.vavr.Predicates.exists;
|
||||
import static io.vavr.Predicates.forAll;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.internal.verification.VerificationModeFactory;
|
||||
import org.mockito.verification.Timeout;
|
||||
|
||||
import io.vavr.Tuple;
|
||||
import io.vavr.Tuple2;
|
||||
import io.vavr.collection.List;
|
||||
import io.vavr.concurrent.Future;
|
||||
import io.vavr.control.Try;
|
||||
|
||||
public class FutureUnitTest {
|
||||
|
||||
private final String SUCCESS = "Success";
|
||||
private final String FAILURE = "Failure";
|
||||
|
||||
@Test
|
||||
public void givenFunctionReturnInteger_WhenCallWithFuture_ShouldReturnFunctionValue() {
|
||||
Future<Integer> future = Future.of(() -> 1);
|
||||
|
||||
assertEquals(1, future.get().intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenFunctionGetRemoteHttpResourceAsString_WhenCallSuccessWithFuture_ShouldReturnContentValueAsString() {
|
||||
String url = "http://resource";
|
||||
String content = "Content from " + url;
|
||||
Future<String> future = Future.of(() -> getResource(url));
|
||||
|
||||
assertEquals(content, future.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenFunctionThrowException_WhenCallWithFuture_ShouldReturnFailure() {
|
||||
Future<String> future = Future.of(() -> getResourceThrowException(""));
|
||||
future.await();
|
||||
|
||||
assertTrue(future.isFailure());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenAFutureReturnZero_WhenCheckFutureWithExistEvenValue_ShouldReturnRight() {
|
||||
Future<Integer> future = Future.of(() -> 2);
|
||||
boolean result = future.exists(i -> i % 2 == 0);
|
||||
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenFunction_WhenCallWithFutureAndRegisterConsumerForSuccess_ShouldCallConsumerToStoreValue() {
|
||||
Future<Integer> future = Future.of(() -> 1);
|
||||
MockConsumer consumer = Mockito.mock(MockConsumer.class);
|
||||
future.onSuccess(consumer);
|
||||
Mockito.verify(consumer, new Timeout(1000, VerificationModeFactory.times(1))).accept(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenFunctionThrowException_WhenCallWithFutureAndRegisterConsumerForFailer_ShouldCallConsumerToStoreException() {
|
||||
Future<String> future = Future.of(() -> getResourceThrowException(""));
|
||||
MockThrowableConsumer consumer = Mockito.mock(MockThrowableConsumer.class);
|
||||
future.onFailure(consumer);
|
||||
Mockito.verify(consumer, new Timeout(1000, VerificationModeFactory.times(1))).accept(Mockito.any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenAFuture_WhenAddAndThenConsumer_ShouldCallConsumerWithResultOfFutureAction() {
|
||||
MockTryConsumer consumer1 = Mockito.mock(MockTryConsumer.class);
|
||||
MockTryConsumer consumer2 = Mockito.mock(MockTryConsumer.class);
|
||||
Future<Integer> future = Future.of(() -> 1);
|
||||
Future<Integer> andThenFuture = future.andThen(consumer1).andThen(consumer2);
|
||||
andThenFuture.await();
|
||||
Mockito.verify(consumer1, VerificationModeFactory.times(1)).accept(Try.success(1));
|
||||
Mockito.verify(consumer2, VerificationModeFactory.times(1)).accept(Try.success(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenAFailureFuture_WhenCallOrElseFunction_ShouldReturnNewFuture() {
|
||||
Future<Integer> future = Future.failed(new RuntimeException());
|
||||
Future<Integer> future2 = future.orElse(Future.of(() -> 2));
|
||||
|
||||
assertEquals(2, future2.get().intValue());
|
||||
}
|
||||
|
||||
@Test(expected = CancellationException.class)
|
||||
public void givenAFuture_WhenCallCancel_ShouldReturnCancellationException() {
|
||||
long waitTime = 1000;
|
||||
Future<Integer> future = Future.of(() -> {
|
||||
Thread.sleep(waitTime);
|
||||
return 1;
|
||||
});
|
||||
future.cancel();
|
||||
future.await();
|
||||
future.get();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenAFuture_WhenCallFallBackWithSuccessFuture_ShouldReturnFutureResult() {
|
||||
String expectedResult = "take this";
|
||||
Future<String> future = Future.of(() -> expectedResult);
|
||||
Future<String> secondFuture = Future.of(() -> "take that");
|
||||
Future<String> futureResult = future.fallbackTo(secondFuture);
|
||||
futureResult.await();
|
||||
|
||||
assertEquals(expectedResult, futureResult.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenAFuture_WhenCallFallBackWithFailureFuture_ShouldReturnValueOfFallBackFuture() {
|
||||
String expectedResult = "take that";
|
||||
Future<String> future = Future.failed(new RuntimeException());
|
||||
Future<String> fallbackFuture = Future.of(() -> expectedResult);
|
||||
Future<String> futureResult = future.fallbackTo(fallbackFuture);
|
||||
|
||||
assertEquals(expectedResult, futureResult.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenAFuture_WhenTransformByAddingOne_ShouldReturn() {
|
||||
Future<Object> future = Future.of(() -> 1).transformValue(f -> Try.of(() -> "Hello: " + f.get()));
|
||||
|
||||
assertEquals("Hello: 1", future.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenAFutureOfInt_WhenMapToString_ShouldCombineAndReturn() {
|
||||
Future<String> future = Future.of(()->1).map(i -> "Hello: " + i);
|
||||
|
||||
assertEquals("Hello: 1", future.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenAFutureOfInt_WhenFlatMapToString_ShouldCombineAndReturn() {
|
||||
Future<Object> futureMap = Future.of(() -> 1).flatMap((i) -> Future.of(() -> "Hello: " + i));
|
||||
|
||||
assertEquals("Hello: 1", futureMap.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenAFutureOf2String_WhenZip_ShouldReturnTupleOf2String() {
|
||||
Future<Tuple2<String, String>> future = Future.of(() -> "hello").zip(Future.of(() -> "world"));
|
||||
|
||||
assertEquals(Tuple.of("hello", "world"), future.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenGetResourceWithFuture_WhenWaitAndMatchWithPredicate_ShouldReturnSuccess() {
|
||||
String url = "http://resource";
|
||||
Future<String> future = Future.of(() -> getResource(url));
|
||||
future.await();
|
||||
String s = Match(future).of(
|
||||
Case($(future0 -> future0.isSuccess()), SUCCESS),
|
||||
Case($(), FAILURE));
|
||||
|
||||
assertEquals(SUCCESS, s);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenAFailedFuture_WhenWaitAndMatchWithPredicateCheckSuccess_ShouldReturnFailed() {
|
||||
Future<Integer> future = Future.failed(new RuntimeException());
|
||||
future.await();
|
||||
String s = Match(future).of(
|
||||
Case($(future0 -> future0.isSuccess()), SUCCESS),
|
||||
Case($(), FAILURE));
|
||||
|
||||
assertEquals(FAILURE, s);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenAFuture_WhenMatchWithFuturePredicate_ShouldReturnSuccess() {
|
||||
Future<Integer> future = Future.of(() -> {
|
||||
Thread.sleep(10);
|
||||
return 1;
|
||||
});
|
||||
Predicate<Future<Integer>> predicate = f -> f.exists(i -> i % 2 == 1);
|
||||
|
||||
String s = Match(future).of(
|
||||
Case($(predicate), "Even"),
|
||||
Case($(), "Odd"));
|
||||
|
||||
assertEquals("Even", s);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenAListOfFutureReturnFist3Integers_WhenMatchWithExistEvenNumberPredicate_ShouldReturnSuccess() {
|
||||
List<Future<Integer>> futures = getFutureOfFirst3Number();
|
||||
Predicate<Future<Integer>> predicate0 = future -> future.exists(i -> i % 2 == 0);
|
||||
String s = Match(futures).of(
|
||||
Case($(exists(predicate0)), "Even"),
|
||||
Case($(), "Odd"));
|
||||
|
||||
assertEquals("Even", s);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenAListOfFutureReturnFist3Integers_WhenMatchWithForAllNumberBiggerThanZeroPredicate_ShouldReturnSuccess() {
|
||||
List<Future<Integer>> futures = getFutureOfFirst3Number();
|
||||
Predicate<Future<Integer>> predicate0 = future -> future.exists(i -> i > 0);
|
||||
String s = Match(futures).of(
|
||||
Case($(forAll(predicate0)), "Positive numbers"),
|
||||
Case($(), "None"));
|
||||
|
||||
assertEquals("Positive numbers", s);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenAListOfFutureReturnFist3Integers_WhenMatchWithForAllNumberSmallerThanZeroPredicate_ShouldReturnFailed() {
|
||||
List<Future<Integer>> futures = getFutureOfFirst3Number();
|
||||
Predicate<Future<Integer>> predicate0 = future -> future.exists(i -> i < 0);
|
||||
String s = Match(futures).of(
|
||||
Case($(forAll(predicate0)), "Negative numbers"),
|
||||
Case($(), "None"));
|
||||
|
||||
assertEquals("None", s);
|
||||
}
|
||||
|
||||
private String getResource(String url) {
|
||||
try {
|
||||
Thread.sleep(10);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return "Content from " + url;
|
||||
}
|
||||
|
||||
private String getResourceThrowException(String url) {
|
||||
throw new RuntimeException("Exception when get resource " + url);
|
||||
}
|
||||
|
||||
private List<Future<Integer>> getFutureOfFirst3Number() {
|
||||
List<Future<Integer>> futures = List.of(Future.of(() -> 1), Future.of(() -> 2), Future.of(() -> 3));
|
||||
return futures;
|
||||
}
|
||||
|
||||
private static void checkOnSuccessFunction() {
|
||||
Future<Integer> future = Future.of(() -> 1);
|
||||
future.onSuccess(i -> System.out.println("Future finish with result: " + i));
|
||||
}
|
||||
|
||||
private static void checkOnFailureFunction() {
|
||||
Future<Integer> future = Future.of(() -> {throw new RuntimeException("Failed");});
|
||||
future.onFailure(t -> System.out.println("Future failures with exception: " + t));
|
||||
}
|
||||
|
||||
private static void runAndThenConsumer() {
|
||||
Future<Integer> future = Future.of(() -> 1);
|
||||
future.andThen(i -> System.out.println("Do side-effect action 1 with input: " + i.get())).
|
||||
andThen((i) -> System.out.println("Do side-effect action 2 with input: " + i.get()));
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
checkOnSuccessFunction();
|
||||
checkOnFailureFunction();
|
||||
runAndThenConsumer();
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class MockConsumer implements Consumer<Integer> {
|
||||
@Override
|
||||
public void accept(Integer t) {
|
||||
}
|
||||
}
|
||||
|
||||
class MockTryConsumer implements Consumer<Try<Integer>> {
|
||||
@Override
|
||||
public void accept(Try<Integer> t) {
|
||||
}
|
||||
}
|
||||
|
||||
class MockThrowableConsumer implements Consumer<Throwable> {
|
||||
@Override
|
||||
public void accept(Throwable t) {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<XMLTutorials>
|
||||
<tutorial tutId="01" type="xml">
|
||||
<title>XML with Dom4J</title>
|
||||
<description>XML handling with Dom4J</description>
|
||||
<date>14/06/2016</date>
|
||||
<author>Dom4J tech writer</author>
|
||||
</tutorial>
|
||||
</XMLTutorials>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<tutorials>
|
||||
<tutorial tutId="01" type="XML">
|
||||
<author>Jaxb author</author>
|
||||
<date>04/02/2015</date>
|
||||
<description>XML Binding with Jaxb</description>
|
||||
<title>XML with Jaxb</title>
|
||||
</tutorial>
|
||||
</tutorials>
|
Loading…
Reference in New Issue