Merge remote-tracking branch 'eugenp/master'
This commit is contained in:
commit
387b92b09a
9
.gitignore
vendored
9
.gitignore
vendored
@ -48,3 +48,12 @@ dependency-reduced-pom.xml
|
|||||||
*.so
|
*.so
|
||||||
*.dylib
|
*.dylib
|
||||||
*.dll
|
*.dll
|
||||||
|
|
||||||
|
xml/src/test/resources/example_dom4j_new.xml
|
||||||
|
xml/src/test/resources/example_dom4j_updated.xml
|
||||||
|
xml/src/test/resources/example_jaxb_new.xml
|
||||||
|
core-java-io/hard_link.txt
|
||||||
|
core-java-io/target_link.txt
|
||||||
|
core-java/src/main/java/com/baeldung/manifest/MANIFEST.MF
|
||||||
|
ethereum/logs/
|
||||||
|
jmeter/src/main/resources/*-JMeter.csv
|
@ -99,6 +99,16 @@
|
|||||||
<artifactId>joda-time</artifactId>
|
<artifactId>joda-time</artifactId>
|
||||||
<version>${joda.version}</version>
|
<version>${joda.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.aspectj</groupId>
|
||||||
|
<artifactId>aspectjrt</artifactId>
|
||||||
|
<version>${asspectj.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.aspectj</groupId>
|
||||||
|
<artifactId>aspectjweaver</artifactId>
|
||||||
|
<version>${asspectj.version}</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
@ -170,6 +180,7 @@
|
|||||||
<joda.version>2.10</joda.version>
|
<joda.version>2.10</joda.version>
|
||||||
<!-- testing -->
|
<!-- testing -->
|
||||||
<assertj.version>3.6.1</assertj.version>
|
<assertj.version>3.6.1</assertj.version>
|
||||||
|
<asspectj.version>1.8.9</asspectj.version>
|
||||||
<avaitility.version>1.7.0</avaitility.version>
|
<avaitility.version>1.7.0</avaitility.version>
|
||||||
<jmh-core.version>1.19</jmh-core.version>
|
<jmh-core.version>1.19</jmh-core.version>
|
||||||
<jmh-generator.version>1.19</jmh-generator.version>
|
<jmh-generator.version>1.19</jmh-generator.version>
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
package com.baeldung.aspect;
|
||||||
|
|
||||||
|
public aspect ChangeCallsToCurrentTimeInMillisMethod {
|
||||||
|
long around():
|
||||||
|
call(public static native long java.lang.System.currentTimeMillis())
|
||||||
|
&& within(user.code.base.pckg.*) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package com.baeldung.util;
|
package com.baeldung.util;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import java.time.Clock;
|
import java.time.Clock;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
@ -9,6 +10,8 @@ import java.time.LocalTime;
|
|||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.time.temporal.ChronoField;
|
import java.time.temporal.ChronoField;
|
||||||
|
|
||||||
|
import org.joda.time.DateTime;
|
||||||
|
import org.joda.time.DateTimeUtils;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class CurrentDateTimeUnitTest {
|
public class CurrentDateTimeUnitTest {
|
||||||
@ -39,5 +42,4 @@ public class CurrentDateTimeUnitTest {
|
|||||||
|
|
||||||
assertEquals(clock.instant().getEpochSecond(), now.getEpochSecond());
|
assertEquals(clock.instant().getEpochSecond(), now.getEpochSecond());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,19 @@
|
|||||||
package com.baeldung.convertlisttomap;
|
package com.baeldung.convertlisttomap;
|
||||||
|
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import org.apache.commons.collections4.MapUtils;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.apache.commons.collections4.IterableUtils;
|
|
||||||
import org.apache.commons.collections4.MapUtils;
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
|
|
||||||
public class ConvertListToMapService {
|
public class ConvertListToMapService {
|
||||||
|
|
||||||
public Map<Integer, Animal> convertListBeforeJava8(List<Animal> list) {
|
public Map<Integer, Animal> convertListBeforeJava8(List<Animal> list) {
|
||||||
Map<Integer, Animal> map = new HashMap<Integer, Animal>();
|
|
||||||
|
Map<Integer, Animal> map = new HashMap<>();
|
||||||
|
|
||||||
for (Animal animal : list) {
|
for (Animal animal : list) {
|
||||||
map.put(animal.getId(), animal);
|
map.put(animal.getId(), animal);
|
||||||
}
|
}
|
||||||
@ -30,20 +31,9 @@ public class ConvertListToMapService {
|
|||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<Integer, Animal> convertListWithApacheCommons1(List<Animal> list) {
|
public Map<Integer, Animal> convertListWithApacheCommons(List<Animal> list) {
|
||||||
|
|
||||||
Map<Integer, Animal> map = new HashMap<Integer, Animal>();
|
Map<Integer, Animal> map = new HashMap<>();
|
||||||
|
|
||||||
IterableUtils.forEach(list, animal -> {
|
|
||||||
map.put(animal.getId(), animal);
|
|
||||||
});
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<Integer, Animal> convertListWithApacheCommons2(List<Animal> list) {
|
|
||||||
|
|
||||||
Map<Integer, Animal> map = new HashMap<Integer, Animal>();
|
|
||||||
|
|
||||||
MapUtils.populateMap(map, list, Animal::getId);
|
MapUtils.populateMap(map, list, Animal::getId);
|
||||||
|
|
@ -1,13 +1,14 @@
|
|||||||
package com.baeldung.convertlisttomap;
|
package com.baeldung.convertlisttomap;
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import org.junit.Before;
|
||||||
import static org.hamcrest.Matchers.*;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.junit.Before;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import org.junit.Test;
|
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||||
|
|
||||||
public class ConvertListToMapServiceUnitTest {
|
public class ConvertListToMapServiceUnitTest {
|
||||||
List<Animal> list;
|
List<Animal> list;
|
||||||
@ -29,6 +30,7 @@ public class ConvertListToMapServiceUnitTest {
|
|||||||
list.add(cow);
|
list.add(cow);
|
||||||
Animal goat = new Animal(5, "Goat");
|
Animal goat = new Animal(5, "Goat");
|
||||||
list.add(goat);
|
list.add(goat);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -56,18 +58,11 @@ public class ConvertListToMapServiceUnitTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenAList_whenConvertWithApacheCommons1_thenReturnMapWithTheSameElements() {
|
public void givenAList_whenConvertWithApacheCommons_thenReturnMapWithTheSameElements() {
|
||||||
|
|
||||||
Map<Integer, Animal> map = convertListService.convertListWithApacheCommons1(list);
|
Map<Integer, Animal> map = convertListService.convertListWithApacheCommons(list);
|
||||||
|
|
||||||
assertThat(map.values(), containsInAnyOrder(list.toArray()));
|
assertThat(map.values(), containsInAnyOrder(list.toArray()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void givenAList_whenConvertWithApacheCommons2_thenReturnMapWithTheSameElements() {
|
|
||||||
|
|
||||||
Map<Integer, Animal> map = convertListService.convertListWithApacheCommons2(list);
|
|
||||||
|
|
||||||
assertThat(map.values(), containsInAnyOrder(list.toArray()));
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
package com.baeldung.convertlisttomap;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.hasItem;
|
||||||
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
|
|
||||||
|
public class ConvertListWithDiplicatedIdToMapServiceUnitTest {
|
||||||
|
List<Animal> duplicatedIdList;
|
||||||
|
|
||||||
|
private ConvertListToMapService convertListService = new ConvertListToMapService();
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void init() {
|
||||||
|
|
||||||
|
this.duplicatedIdList = new ArrayList<>();
|
||||||
|
|
||||||
|
Animal cat = new Animal(1, "Cat");
|
||||||
|
duplicatedIdList.add(cat);
|
||||||
|
Animal dog = new Animal(2, "Dog");
|
||||||
|
duplicatedIdList.add(dog);
|
||||||
|
Animal pig = new Animal(3, "Pig");
|
||||||
|
duplicatedIdList.add(pig);
|
||||||
|
Animal cow = new Animal(4, "Cow");
|
||||||
|
duplicatedIdList.add(cow);
|
||||||
|
Animal goat = new Animal(4, "Goat");
|
||||||
|
duplicatedIdList.add(goat);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenADupIdList_whenConvertBeforeJava8_thenReturnMapWithRewrittenElement() {
|
||||||
|
|
||||||
|
Map<Integer, Animal> map = convertListService.convertListBeforeJava8(duplicatedIdList);
|
||||||
|
|
||||||
|
assertThat(map.values(), hasSize(4));
|
||||||
|
assertThat(map.values(), hasItem(duplicatedIdList.get(4)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenADupIdList_whenConvertWithApacheCommons_thenReturnMapWithRewrittenElement() {
|
||||||
|
|
||||||
|
Map<Integer, Animal> map = convertListService.convertListWithApacheCommons(duplicatedIdList);
|
||||||
|
|
||||||
|
assertThat(map.values(), hasSize(4));
|
||||||
|
assertThat(map.values(), hasItem(duplicatedIdList.get(4)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalStateException.class)
|
||||||
|
public void givenADupIdList_whenConvertAfterJava8_thenException() {
|
||||||
|
|
||||||
|
convertListService.convertListAfterJava8(duplicatedIdList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void givenADupIdList_whenConvertWithGuava_thenException() {
|
||||||
|
|
||||||
|
convertListService.convertListWithGuava(duplicatedIdList);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -147,6 +147,17 @@
|
|||||||
<artifactId>icu4j</artifactId>
|
<artifactId>icu4j</artifactId>
|
||||||
<version>${icu4j.version}</version>
|
<version>${icu4j.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- Mime Type Resolution Libraries -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.tika</groupId>
|
||||||
|
<artifactId>tika-core</artifactId>
|
||||||
|
<version>${tika.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.sf.jmimemagic</groupId>
|
||||||
|
<artifactId>jmimemagic</artifactId>
|
||||||
|
<version>${jmime-magic.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
<artifactId>commons-dbcp2</artifactId>
|
<artifactId>commons-dbcp2</artifactId>
|
||||||
@ -162,6 +173,19 @@
|
|||||||
<artifactId>c3p0</artifactId>
|
<artifactId>c3p0</artifactId>
|
||||||
<version>${c3p0.version}</version>
|
<version>${c3p0.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- instrumentation -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.javassist</groupId>
|
||||||
|
<artifactId>javassist</artifactId>
|
||||||
|
<version>${javaassist.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.sun</groupId>
|
||||||
|
<artifactId>tools</artifactId>
|
||||||
|
<version>1.8.0</version>
|
||||||
|
<scope>system</scope>
|
||||||
|
<systemPath>${java.home}/../lib/tools.jar</systemPath>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
@ -389,6 +413,111 @@
|
|||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</profile>
|
</profile>
|
||||||
|
|
||||||
|
<!-- java instrumentation profiles to build jars -->
|
||||||
|
<profile>
|
||||||
|
<id>buildAgentLoader</id>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>jar</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<classifier>agentLoader</classifier>
|
||||||
|
<classesDirectory>target/classes</classesDirectory>
|
||||||
|
<archive>
|
||||||
|
<manifest>
|
||||||
|
<addClasspath>true</addClasspath>
|
||||||
|
</manifest>
|
||||||
|
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
|
||||||
|
</archive>
|
||||||
|
|
||||||
|
<includes>
|
||||||
|
<include>com/baeldung/instrumentation/application/AgentLoader.class</include>
|
||||||
|
<include>com/baeldung/instrumentation/application/Launcher.class</include>
|
||||||
|
</includes>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</profile>
|
||||||
|
<profile>
|
||||||
|
<id>buildApplication</id>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>jar</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<classifier>application</classifier>
|
||||||
|
<classesDirectory>target/classes</classesDirectory>
|
||||||
|
<archive>
|
||||||
|
<manifest>
|
||||||
|
<addClasspath>true</addClasspath>
|
||||||
|
</manifest>
|
||||||
|
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
|
||||||
|
</archive>
|
||||||
|
|
||||||
|
<includes>
|
||||||
|
<include>com/baeldung/instrumentation/application/MyAtm.class</include>
|
||||||
|
<include>com/baeldung/instrumentation/application/MyAtmApplication.class</include>
|
||||||
|
<include>com/baeldung/instrumentation/application/Launcher.class</include>
|
||||||
|
</includes>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</profile>
|
||||||
|
<profile>
|
||||||
|
<id>buildAgent</id>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>jar</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<classifier>agent</classifier>
|
||||||
|
<classesDirectory>target/classes</classesDirectory>
|
||||||
|
<archive>
|
||||||
|
<manifest>
|
||||||
|
<addClasspath>true</addClasspath>
|
||||||
|
</manifest>
|
||||||
|
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
|
||||||
|
</archive>
|
||||||
|
|
||||||
|
<includes>
|
||||||
|
<include>com/baeldung/instrumentation/agent/AtmTransformer.class</include>
|
||||||
|
<include>com/baeldung/instrumentation/agent/MyInstrumentationAgent.class</include>
|
||||||
|
</includes>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</profile>
|
||||||
</profiles>
|
</profiles>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
@ -439,6 +568,11 @@
|
|||||||
<spring-boot-maven-plugin.version>2.0.3.RELEASE</spring-boot-maven-plugin.version>
|
<spring-boot-maven-plugin.version>2.0.3.RELEASE</spring-boot-maven-plugin.version>
|
||||||
<exec-maven-plugin.version>1.6.0</exec-maven-plugin.version>
|
<exec-maven-plugin.version>1.6.0</exec-maven-plugin.version>
|
||||||
<icu4j.version>61.1</icu4j.version>
|
<icu4j.version>61.1</icu4j.version>
|
||||||
|
<!-- Mime Type Libraries -->
|
||||||
|
<tika.version>1.18</tika.version>
|
||||||
|
<jmime-magic.version>0.1.5</jmime-magic.version>
|
||||||
|
<!-- instrumentation -->
|
||||||
|
<javaassist.version>3.21.0-GA</javaassist.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -33,7 +33,7 @@ public class BasicConnectionPool implements ConnectionPool {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Connection getConnection() throws SQLException {
|
public Connection getConnection() throws SQLException {
|
||||||
if (connectionPool.size() == 0) {
|
if (connectionPool.isEmpty()) {
|
||||||
if (usedConnections.size() < MAX_POOL_SIZE) {
|
if (usedConnections.size() < MAX_POOL_SIZE) {
|
||||||
connectionPool.add(createConnection(url, user, password));
|
connectionPool.add(createConnection(url, user, password));
|
||||||
} else {
|
} else {
|
||||||
@ -76,9 +76,7 @@ public class BasicConnectionPool implements ConnectionPool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void shutdown() throws SQLException {
|
public void shutdown() throws SQLException {
|
||||||
for (Connection c : usedConnections) {
|
usedConnections.forEach(this::releaseConnection);
|
||||||
this.releaseConnection(c);
|
|
||||||
}
|
|
||||||
for (Connection c : connectionPool) {
|
for (Connection c : connectionPool) {
|
||||||
c.close();
|
c.close();
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,70 @@
|
|||||||
|
package com.baeldung.instrumentation.agent;
|
||||||
|
|
||||||
|
import javassist.CannotCompileException;
|
||||||
|
import javassist.ClassPool;
|
||||||
|
import javassist.CtClass;
|
||||||
|
import javassist.CtMethod;
|
||||||
|
import javassist.NotFoundException;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.instrument.ClassFileTransformer;
|
||||||
|
import java.lang.instrument.IllegalClassFormatException;
|
||||||
|
import java.security.ProtectionDomain;
|
||||||
|
|
||||||
|
public class AtmTransformer implements ClassFileTransformer {
|
||||||
|
|
||||||
|
private static Logger LOGGER = LoggerFactory.getLogger(AtmTransformer.class);
|
||||||
|
|
||||||
|
private static final String WITHDRAW_MONEY_METHOD = "withdrawMoney";
|
||||||
|
|
||||||
|
/** The internal form class name of the class to transform */
|
||||||
|
private String targetClassName;
|
||||||
|
/** The class loader of the class we want to transform */
|
||||||
|
private ClassLoader targetClassLoader;
|
||||||
|
|
||||||
|
public AtmTransformer(String targetClassName, ClassLoader targetClassLoader) {
|
||||||
|
this.targetClassName = targetClassName;
|
||||||
|
this.targetClassLoader = targetClassLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
|
||||||
|
ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
|
||||||
|
byte[] byteCode = classfileBuffer;
|
||||||
|
|
||||||
|
String finalTargetClassName = this.targetClassName.replaceAll("\\.", "/"); //replace . with /
|
||||||
|
if (!className.equals(finalTargetClassName)) {
|
||||||
|
return byteCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (className.equals(finalTargetClassName) && loader.equals(targetClassLoader)) {
|
||||||
|
LOGGER.info("[Agent] Transforming class MyAtm");
|
||||||
|
try {
|
||||||
|
ClassPool cp = ClassPool.getDefault();
|
||||||
|
CtClass cc = cp.get(targetClassName);
|
||||||
|
CtMethod m = cc.getDeclaredMethod(WITHDRAW_MONEY_METHOD);
|
||||||
|
m.addLocalVariable("startTime", CtClass.longType);
|
||||||
|
m.insertBefore("startTime = System.currentTimeMillis();");
|
||||||
|
|
||||||
|
StringBuilder endBlock = new StringBuilder();
|
||||||
|
|
||||||
|
m.addLocalVariable("endTime", CtClass.longType);
|
||||||
|
m.addLocalVariable("opTime", CtClass.longType);
|
||||||
|
endBlock.append("endTime = System.currentTimeMillis();");
|
||||||
|
endBlock.append("opTime = (endTime-startTime)/1000;");
|
||||||
|
|
||||||
|
endBlock.append("LOGGER.info(\"[Application] Withdrawal operation completed in:\" + opTime + \" seconds!\");");
|
||||||
|
|
||||||
|
m.insertAfter(endBlock.toString());
|
||||||
|
|
||||||
|
byteCode = cc.toBytecode();
|
||||||
|
cc.detach();
|
||||||
|
} catch (NotFoundException | CannotCompileException | IOException e) {
|
||||||
|
LOGGER.error("Exception", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return byteCode;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
package com.baeldung.instrumentation.agent;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.lang.instrument.Instrumentation;
|
||||||
|
|
||||||
|
public class MyInstrumentationAgent {
|
||||||
|
private static Logger LOGGER = LoggerFactory.getLogger(MyInstrumentationAgent.class);
|
||||||
|
|
||||||
|
public static void premain(String agentArgs, Instrumentation inst) {
|
||||||
|
LOGGER.info("[Agent] In premain method");
|
||||||
|
|
||||||
|
String className = "com.baeldung.instrumentation.application.MyAtm";
|
||||||
|
transformClass(className,inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void agentmain(String agentArgs, Instrumentation inst) {
|
||||||
|
LOGGER.info("[Agent] In agentmain method");
|
||||||
|
|
||||||
|
String className = "com.baeldung.instrumentation.application.MyAtm";
|
||||||
|
transformClass(className,inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void transformClass(String className, Instrumentation instrumentation) {
|
||||||
|
Class<?> targetCls = null;
|
||||||
|
ClassLoader targetClassLoader = null;
|
||||||
|
// see if we can get the class using forName
|
||||||
|
try {
|
||||||
|
targetCls = Class.forName(className);
|
||||||
|
targetClassLoader = targetCls.getClassLoader();
|
||||||
|
transform(targetCls, targetClassLoader, instrumentation);
|
||||||
|
return;
|
||||||
|
} catch (Exception ex) {
|
||||||
|
LOGGER.error("Class [{}] not found with Class.forName");
|
||||||
|
}
|
||||||
|
// otherwise iterate all loaded classes and find what we want
|
||||||
|
for(Class<?> clazz: instrumentation.getAllLoadedClasses()) {
|
||||||
|
if(clazz.getName().equals(className)) {
|
||||||
|
targetCls = clazz;
|
||||||
|
targetClassLoader = targetCls.getClassLoader();
|
||||||
|
transform(targetCls, targetClassLoader, instrumentation);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new RuntimeException("Failed to find class [" + className + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void transform(Class<?> clazz, ClassLoader classLoader, Instrumentation instrumentation) {
|
||||||
|
AtmTransformer dt = new AtmTransformer(clazz.getName(), classLoader);
|
||||||
|
instrumentation.addTransformer(dt, true);
|
||||||
|
try {
|
||||||
|
instrumentation.retransformClasses(clazz);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new RuntimeException("Transform failed for class: [" + clazz.getName() + "]", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
package com.baeldung.instrumentation.application;
|
||||||
|
|
||||||
|
import com.sun.tools.attach.VirtualMachine;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by adi on 6/10/18.
|
||||||
|
*/
|
||||||
|
public class AgentLoader {
|
||||||
|
private static Logger LOGGER = LoggerFactory.getLogger(AgentLoader.class);
|
||||||
|
|
||||||
|
public static void run(String[] args) {
|
||||||
|
String agentFilePath = "/home/adi/Desktop/agent-1.0.0-jar-with-dependencies.jar";
|
||||||
|
String applicationName = "MyAtmApplication";
|
||||||
|
|
||||||
|
//iterate all jvms and get the first one that matches our application name
|
||||||
|
Optional<String> jvmProcessOpt = Optional.ofNullable(VirtualMachine.list()
|
||||||
|
.stream()
|
||||||
|
.filter(jvm -> {
|
||||||
|
LOGGER.info("jvm:{}", jvm.displayName());
|
||||||
|
return jvm.displayName().contains(applicationName);
|
||||||
|
})
|
||||||
|
.findFirst().get().id());
|
||||||
|
|
||||||
|
if(!jvmProcessOpt.isPresent()) {
|
||||||
|
LOGGER.error("Target Application not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
File agentFile = new File(agentFilePath);
|
||||||
|
try {
|
||||||
|
String jvmPid = jvmProcessOpt.get();
|
||||||
|
LOGGER.info("Attaching to target JVM with PID: " + jvmPid);
|
||||||
|
VirtualMachine jvm = VirtualMachine.attach(jvmPid);
|
||||||
|
jvm.loadAgent(agentFile.getAbsolutePath());
|
||||||
|
jvm.detach();
|
||||||
|
LOGGER.info("Attached to target JVM and loaded Java agent successfully");
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.baeldung.instrumentation.application;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by adi on 6/14/18.
|
||||||
|
*/
|
||||||
|
public class Launcher {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
if(args[0].equals("StartMyAtmApplication")) {
|
||||||
|
new MyAtmApplication().run(args);
|
||||||
|
} else if(args[0].equals("LoadAgent")) {
|
||||||
|
new AgentLoader().run(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.baeldung.instrumentation.application;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by adi on 6/11/18.
|
||||||
|
*/
|
||||||
|
public class MyAtm {
|
||||||
|
private static Logger LOGGER = LoggerFactory.getLogger(MyAtm.class);
|
||||||
|
|
||||||
|
private static final int account = 10;
|
||||||
|
|
||||||
|
public static void withdrawMoney(int amount) throws InterruptedException {
|
||||||
|
Thread.sleep(2000l); //processing going on here
|
||||||
|
LOGGER.info("[Application] Successful Withdrawal of [{}] units!", amount);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.baeldung.instrumentation.application;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class MyAtmApplication {
|
||||||
|
|
||||||
|
private static Logger LOGGER = LoggerFactory.getLogger(MyAtmApplication.class);
|
||||||
|
|
||||||
|
public static void run(String[] args) throws Exception {
|
||||||
|
LOGGER.info("[Application] Starting ATM application");
|
||||||
|
MyAtm.withdrawMoney(Integer.parseInt(args[2]));
|
||||||
|
|
||||||
|
Thread.sleep(Long.valueOf(args[1]));
|
||||||
|
|
||||||
|
MyAtm.withdrawMoney(Integer.parseInt(args[3]));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
5
core-java/src/main/resources/META-INF/MANIFEST.MF
Normal file
5
core-java/src/main/resources/META-INF/MANIFEST.MF
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
Agent-Class: com.baeldung.instrumentation.agent.MyInstrumentationAgent
|
||||||
|
Can-Redefine-Classes: true
|
||||||
|
Can-Retransform-Classes: true
|
||||||
|
Premain-Class: com.baeldung.instrumentation.agent.MyInstrumentationAgent
|
||||||
|
Main-Class: com.baeldung.instrumentation.application.Launcher
|
13
core-java/src/main/resources/log4j2.xml
Normal file
13
core-java/src/main/resources/log4j2.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Configuration status="WARN">
|
||||||
|
<Appenders>
|
||||||
|
<Console name="Console" target="SYSTEM_OUT">
|
||||||
|
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level - %msg%n"/>
|
||||||
|
</Console>
|
||||||
|
</Appenders>
|
||||||
|
<Loggers>
|
||||||
|
<Root level="debug">
|
||||||
|
<AppenderRef ref="Console"/>
|
||||||
|
</Root>
|
||||||
|
</Loggers>
|
||||||
|
</Configuration>
|
BIN
core-java/src/main/resources/product.png
Normal file
BIN
core-java/src/main/resources/product.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 54 KiB |
@ -0,0 +1,131 @@
|
|||||||
|
package com.baeldung.java.mimetype;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.FileNameMap;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import javax.activation.MimetypesFileTypeMap;
|
||||||
|
|
||||||
|
import org.apache.tika.Tika;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import net.sf.jmimemagic.Magic;
|
||||||
|
import net.sf.jmimemagic.MagicException;
|
||||||
|
import net.sf.jmimemagic.MagicMatch;
|
||||||
|
import net.sf.jmimemagic.MagicMatchNotFoundException;
|
||||||
|
import net.sf.jmimemagic.MagicParseException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test class demonstrating various strategies to resolve MIME type of a file.
|
||||||
|
* @author tritty
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class MimeTypeUnitTest {
|
||||||
|
/**
|
||||||
|
* Expected Ouput.
|
||||||
|
*/
|
||||||
|
public static final String PNG_EXT = "image/png";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The location of the file.
|
||||||
|
*/
|
||||||
|
public static final String FILE_LOC = "src/test/resources/product.png";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test method, demonstrating usage in Java 7.
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void whenUsingJava7_thenSuccess() throws IOException {
|
||||||
|
final Path path = new File(FILE_LOC).toPath();
|
||||||
|
final String mimeType = Files.probeContentType(path);
|
||||||
|
assertEquals(mimeType, PNG_EXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test method demonstrating the usage of URLConnection to resolve MIME type.
|
||||||
|
*
|
||||||
|
* @throws MalformedURLException
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void whenUsingGetContentType_thenSuccess() throws MalformedURLException, IOException {
|
||||||
|
final File file = new File(FILE_LOC);
|
||||||
|
final URLConnection connection = file.toURL()
|
||||||
|
.openConnection();
|
||||||
|
final String mimeType = connection.getContentType();
|
||||||
|
assertEquals(mimeType, PNG_EXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test method demonstrating the usage of URLConnection to resolve MIME type.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void whenUsingGuessContentTypeFromName_thenSuccess() {
|
||||||
|
final File file = new File(FILE_LOC);
|
||||||
|
final String mimeType = URLConnection.guessContentTypeFromName(file.getName());
|
||||||
|
assertEquals(mimeType, PNG_EXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test method demonstrating the usage of FileNameMap from URLConnection
|
||||||
|
* to resolve MIME type of a file.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void whenUsingGetFileNameMap_thenSuccess() {
|
||||||
|
final File file = new File(FILE_LOC);
|
||||||
|
final FileNameMap fileNameMap = URLConnection.getFileNameMap();
|
||||||
|
final String mimeType = fileNameMap.getContentTypeFor(file.getName());
|
||||||
|
assertEquals(mimeType, PNG_EXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test method demonstrating the usage of MimeTypesFileTypeMap for resolution of
|
||||||
|
* MIME type.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void whenUsingMimeTypesFileTypeMap_thenSuccess() {
|
||||||
|
final File file = new File(FILE_LOC);
|
||||||
|
final MimetypesFileTypeMap fileTypeMap = new MimetypesFileTypeMap();
|
||||||
|
final String mimeType = fileTypeMap.getContentType(file.getName());
|
||||||
|
assertEquals(mimeType, PNG_EXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test method demonstrating usage of jMimeMagic.
|
||||||
|
*
|
||||||
|
* @throws MagicParseException
|
||||||
|
* @throws MagicMatchNotFoundException
|
||||||
|
* @throws MagicException
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void whenUsingJmimeMagic_thenSuccess() throws MagicParseException, MagicMatchNotFoundException, MagicException {
|
||||||
|
final File file = new File(FILE_LOC);
|
||||||
|
final Magic magic = new Magic();
|
||||||
|
final MagicMatch match = magic.getMagicMatch(file, false);
|
||||||
|
assertEquals(match.getMimeType(), PNG_EXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test method demonstrating usage of Apache Tika.
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void whenUsingTika_thenSuccess() throws IOException {
|
||||||
|
final File file = new File(FILE_LOC);
|
||||||
|
final Tika tika = new Tika();
|
||||||
|
final String mimeType = tika.detect(file);
|
||||||
|
assertEquals(mimeType, PNG_EXT);
|
||||||
|
}
|
||||||
|
}
|
1588
core-java/src/test/resources/META-INF/mime.types
Normal file
1588
core-java/src/test/resources/META-INF/mime.types
Normal file
File diff suppressed because it is too large
Load Diff
BIN
core-java/src/test/resources/product.png
Normal file
BIN
core-java/src/test/resources/product.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 54 KiB |
@ -347,7 +347,7 @@
|
|||||||
</plugins>
|
</plugins>
|
||||||
</pluginManagement>
|
</pluginManagement>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<!-- <plugin>
|
||||||
<groupId>com.github.ekryd.sortpom</groupId>
|
<groupId>com.github.ekryd.sortpom</groupId>
|
||||||
<artifactId>sortpom-maven-plugin</artifactId>
|
<artifactId>sortpom-maven-plugin</artifactId>
|
||||||
<version>${sortpom-maven-plugin.version}</version>
|
<version>${sortpom-maven-plugin.version}</version>
|
||||||
@ -367,7 +367,7 @@
|
|||||||
<keepBlankLines>true</keepBlankLines>
|
<keepBlankLines>true</keepBlankLines>
|
||||||
<expandEmptyElements>false</expandEmptyElements>
|
<expandEmptyElements>false</expandEmptyElements>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin> -->
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>com.spotify</groupId>
|
<groupId>com.spotify</groupId>
|
||||||
<artifactId>docker-maven-plugin</artifactId>
|
<artifactId>docker-maven-plugin</artifactId>
|
||||||
|
5
pom.xml
5
pom.xml
@ -433,6 +433,7 @@
|
|||||||
<module>spring-data-couchbase-2</module>
|
<module>spring-data-couchbase-2</module>
|
||||||
<module>persistence-modules/spring-data-dynamodb</module>
|
<module>persistence-modules/spring-data-dynamodb</module>
|
||||||
<module>spring-data-elasticsearch</module>
|
<module>spring-data-elasticsearch</module>
|
||||||
|
<module>spring-data-jpa</module>
|
||||||
<module>spring-data-keyvalue</module>
|
<module>spring-data-keyvalue</module>
|
||||||
<module>spring-data-mongodb</module>
|
<module>spring-data-mongodb</module>
|
||||||
<module>persistence-modules/spring-data-neo4j</module>
|
<module>persistence-modules/spring-data-neo4j</module>
|
||||||
@ -547,6 +548,7 @@
|
|||||||
<module>apache-meecrowave</module>
|
<module>apache-meecrowave</module>
|
||||||
<module>spring-reactive-kotlin</module>
|
<module>spring-reactive-kotlin</module>
|
||||||
<module>jnosql</module>
|
<module>jnosql</module>
|
||||||
|
<module>testing-modules/junit-abstract</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
</profile>
|
</profile>
|
||||||
@ -666,6 +668,7 @@
|
|||||||
<module>spring-amqp-simple</module>
|
<module>spring-amqp-simple</module>
|
||||||
<module>spring-apache-camel</module>
|
<module>spring-apache-camel</module>
|
||||||
<module>spring-batch</module>
|
<module>spring-batch</module>
|
||||||
|
<module>testing-modules/junit-abstract</module>
|
||||||
|
|
||||||
|
|
||||||
<!-- group 2 - Pass, 11-16 min, 42 test failures, 4,020 KB -->
|
<!-- group 2 - Pass, 11-16 min, 42 test failures, 4,020 KB -->
|
||||||
@ -1071,7 +1074,7 @@
|
|||||||
<module>antlr</module>
|
<module>antlr</module>
|
||||||
<module>maven-archetype</module>
|
<module>maven-archetype</module>
|
||||||
<module>apache-meecrowave</module>
|
<module>apache-meecrowave</module>
|
||||||
|
<module>testing-modules/junit-abstract</module>
|
||||||
|
|
||||||
<!-- problematic -->
|
<!-- problematic -->
|
||||||
<!--
|
<!--
|
||||||
|
26
spring-data-jpa/pom.xml
Normal file
26
spring-data-jpa/pom.xml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?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">
|
||||||
|
<parent>
|
||||||
|
<artifactId>parent-boot-2</artifactId>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<relativePath>../parent-boot-2</relativePath>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>spring-data-jpa</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.h2database</groupId>
|
||||||
|
<artifactId>h2</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
14
spring-data-jpa/src/main/java/com/baeldung/Application.java
Normal file
14
spring-data-jpa/src/main/java/com/baeldung/Application.java
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package com.baeldung;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class Application {
|
||||||
|
private static ApplicationContext applicationContext;
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
applicationContext = SpringApplication.run(Application.class, args);
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package com.baeldung.repository;
|
package com.baeldung.repository;
|
||||||
|
|
||||||
import com.baeldung.domain.Article;
|
import com.baeldung.domain.Article;
|
||||||
|
import com.baeldung.repository.ArticleRepository;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
15
spring-data-jpa/src/test/resources/application.properties
Normal file
15
spring-data-jpa/src/test/resources/application.properties
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
spring.datasource.data=import_articles.sql
|
@ -45,4 +45,19 @@ public class FooController {
|
|||||||
.setName("Foo Name")
|
.setName("Foo Name")
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequestMapping(method = RequestMethod.POST, value = "/foos/new")
|
||||||
|
@ResponseStatus(HttpStatus.OK)
|
||||||
|
@ResponseBody
|
||||||
|
public Foo createFoo(@RequestBody final Foo foo) {
|
||||||
|
return foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(method = RequestMethod.DELETE, value = "/foos/{id}")
|
||||||
|
@ResponseStatus(HttpStatus.OK)
|
||||||
|
@ResponseBody
|
||||||
|
public long deleteFoo(@PathVariable final long id) {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,9 +10,9 @@
|
|||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.baeldung</groupId>
|
<groupId>com.baeldung</groupId>
|
||||||
<artifactId>parent-spring-4</artifactId>
|
<artifactId>parent-spring-5</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
<relativePath>../parent-spring-4</relativePath>
|
<relativePath>../parent-spring-5</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@ -22,12 +22,12 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.security</groupId>
|
<groupId>org.springframework.security</groupId>
|
||||||
<artifactId>spring-security-web</artifactId>
|
<artifactId>spring-security-web</artifactId>
|
||||||
<version>${org.springframework.security.version}</version>
|
<version>${spring.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.security</groupId>
|
<groupId>org.springframework.security</groupId>
|
||||||
<artifactId>spring-security-config</artifactId>
|
<artifactId>spring-security-config</artifactId>
|
||||||
<version>${org.springframework.security.version}</version>
|
<version>${spring.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- Spring -->
|
<!-- Spring -->
|
||||||
@ -96,7 +96,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.fasterxml.jackson.core</groupId>
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
<artifactId>jackson-databind</artifactId>
|
<artifactId>jackson-databind</artifactId>
|
||||||
<version>${jackson.version}</version>
|
<version>${jackson-databind.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- http -->
|
<!-- http -->
|
||||||
@ -270,8 +270,6 @@
|
|||||||
</profiles>
|
</profiles>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<!-- Spring -->
|
|
||||||
<org.springframework.security.version>4.2.6.RELEASE</org.springframework.security.version>
|
|
||||||
|
|
||||||
<!-- http -->
|
<!-- http -->
|
||||||
<httpcore.version>4.4.5</httpcore.version>
|
<httpcore.version>4.4.5</httpcore.version>
|
||||||
@ -280,7 +278,6 @@
|
|||||||
<!-- various -->
|
<!-- various -->
|
||||||
<jstl.version>1.2</jstl.version>
|
<jstl.version>1.2</jstl.version>
|
||||||
<javax.servlet.version>3.1.0</javax.servlet.version>
|
<javax.servlet.version>3.1.0</javax.servlet.version>
|
||||||
<jackson.version>2.8.5</jackson.version>
|
|
||||||
|
|
||||||
<!-- util -->
|
<!-- util -->
|
||||||
<guava.version>19.0</guava.version>
|
<guava.version>19.0</guava.version>
|
||||||
|
@ -2,11 +2,14 @@ package org.baeldung.filter;
|
|||||||
|
|
||||||
import org.baeldung.security.RestAuthenticationEntryPoint;
|
import org.baeldung.security.RestAuthenticationEntryPoint;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
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.EnableWebSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ -20,7 +23,7 @@ public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAda
|
|||||||
auth
|
auth
|
||||||
.inMemoryAuthentication()
|
.inMemoryAuthentication()
|
||||||
.withUser("user1")
|
.withUser("user1")
|
||||||
.password("user1Pass")
|
.password(passwordEncoder().encode("user1Pass"))
|
||||||
.authorities("ROLE_USER");
|
.authorities("ROLE_USER");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,4 +41,9 @@ public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAda
|
|||||||
|
|
||||||
http.addFilterAfter(new CustomFilter(), BasicAuthenticationFilter.class);
|
http.addFilterAfter(new CustomFilter(), BasicAuthenticationFilter.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public PasswordEncoder passwordEncoder() {
|
||||||
|
return new BCryptPasswordEncoder();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,12 +7,13 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
import org.springframework.http.converter.HttpMessageConverter;
|
import org.springframework.http.converter.HttpMessageConverter;
|
||||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebMvc
|
@EnableWebMvc
|
||||||
@ComponentScan("org.baeldung.web")
|
@ComponentScan("org.baeldung.web")
|
||||||
public class WebConfig extends WebMvcConfigurerAdapter {
|
public class WebConfig implements WebMvcConfigurer {
|
||||||
|
|
||||||
public WebConfig() {
|
public WebConfig() {
|
||||||
super();
|
super();
|
||||||
@ -22,7 +23,6 @@ public class WebConfig extends WebMvcConfigurerAdapter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configureMessageConverters(final List<HttpMessageConverter<?>> converters) {
|
public void configureMessageConverters(final List<HttpMessageConverter<?>> converters) {
|
||||||
super.configureMessageConverters(converters);
|
|
||||||
converters.add(new MappingJackson2HttpMessageConverter());
|
converters.add(new MappingJackson2HttpMessageConverter());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,25 +2,27 @@
|
|||||||
<beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
|
<beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
|
||||||
xsi:schemaLocation="
|
xsi:schemaLocation="
|
||||||
http://www.springframework.org/schema/security
|
http://www.springframework.org/schema/security
|
||||||
http://www.springframework.org/schema/security/spring-security-4.2.xsd
|
http://www.springframework.org/schema/security/spring-security.xsd
|
||||||
http://www.springframework.org/schema/beans
|
http://www.springframework.org/schema/beans
|
||||||
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd"
|
http://www.springframework.org/schema/beans/spring-beans.xsd"
|
||||||
>
|
>
|
||||||
|
|
||||||
<http create-session="stateless" use-expressions="true">
|
<http create-session="stateless" use-expressions="true">
|
||||||
|
|
||||||
<http-basic/>
|
<http-basic entry-point-ref="myBasicAuthenticationEntryPoint"/>
|
||||||
|
|
||||||
</http>
|
</http>
|
||||||
|
|
||||||
<authentication-manager>
|
<authentication-manager>
|
||||||
<authentication-provider>
|
<authentication-provider>
|
||||||
<user-service>
|
<user-service>
|
||||||
<user name="user1" password="user1Pass" authorities="ROLE_USER"/>
|
<user name="user1" password="{noop}user1Pass" authorities="ROLE_USER"/>
|
||||||
</user-service>
|
</user-service>
|
||||||
</authentication-provider>
|
</authentication-provider>
|
||||||
</authentication-manager>
|
</authentication-manager>
|
||||||
|
|
||||||
<global-method-security pre-post-annotations="enabled"/>
|
<global-method-security pre-post-annotations="enabled"/>
|
||||||
|
|
||||||
|
<beans:bean id="myBasicAuthenticationEntryPoint" class="org.baeldung.basic.MyBasicAuthenticationEntryPoint" />
|
||||||
|
|
||||||
</beans:beans>
|
</beans:beans>
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd" >
|
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" >
|
||||||
|
|
||||||
</beans>
|
</beans>
|
@ -8,6 +8,9 @@ import java.io.IOException;
|
|||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLException;
|
||||||
|
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||||
|
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
import org.apache.http.client.ClientProtocolException;
|
import org.apache.http.client.ClientProtocolException;
|
||||||
import org.apache.http.client.methods.HttpGet;
|
import org.apache.http.client.methods.HttpGet;
|
||||||
@ -16,6 +19,7 @@ import org.apache.http.conn.ssl.NoopHostnameVerifier;
|
|||||||
import org.apache.http.conn.ssl.SSLSocketFactory;
|
import org.apache.http.conn.ssl.SSLSocketFactory;
|
||||||
import org.apache.http.conn.ssl.TrustStrategy;
|
import org.apache.http.conn.ssl.TrustStrategy;
|
||||||
import org.apache.http.impl.client.CloseableHttpClient;
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.DefaultHttpClient;
|
||||||
import org.apache.http.impl.client.HttpClients;
|
import org.apache.http.impl.client.HttpClients;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -10,6 +10,10 @@
|
|||||||
<name>spring-session-jdbc</name>
|
<name>spring-session-jdbc</name>
|
||||||
<description>Spring Session with JDBC tutorial</description>
|
<description>Spring Session with JDBC tutorial</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<h2.version>1.4.197</h2.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>parent-boot-2</artifactId>
|
<artifactId>parent-boot-2</artifactId>
|
||||||
<groupId>com.baeldung</groupId>
|
<groupId>com.baeldung</groupId>
|
||||||
@ -18,25 +22,18 @@
|
|||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.session</groupId>
|
<groupId>org.springframework.session</groupId>
|
||||||
<artifactId>spring-session-core</artifactId>
|
<artifactId>spring-session-jdbc</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.h2database</groupId>
|
<groupId>com.h2database</groupId>
|
||||||
<artifactId>h2</artifactId>
|
<artifactId>h2</artifactId>
|
||||||
|
<version>${h2.version}</version>
|
||||||
<scope>runtime</scope>
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -44,14 +41,6 @@
|
|||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.session</groupId>
|
|
||||||
<artifactId>spring-session-jdbc</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<!--<dependency>-->
|
|
||||||
<!--<groupId>org.springframework.boot</groupId>-->
|
|
||||||
<!--<artifactId>spring-boot-starter-security</artifactId>-->
|
|
||||||
<!--</dependency>-->
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
@ -24,20 +24,19 @@ public class SpringSessionJdbcController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/saveColor")
|
@PostMapping("/saveColor")
|
||||||
public String saveMessage(@RequestParam("color") String color,
|
public String saveMessage(@RequestParam("color") String color, HttpServletRequest request) {
|
||||||
HttpServletRequest request) {
|
|
||||||
List<String> favoriteColors = getFavColors(request.getSession());
|
List<String> favoriteColors = getFavColors(request.getSession());
|
||||||
if (!StringUtils.isEmpty(color)) {
|
if (!StringUtils.isEmpty(color)) {
|
||||||
favoriteColors.add(color);
|
favoriteColors.add(color);
|
||||||
request.getSession().
|
request
|
||||||
setAttribute("favoriteColors", favoriteColors);
|
.getSession()
|
||||||
|
.setAttribute("favoriteColors", favoriteColors);
|
||||||
}
|
}
|
||||||
return "redirect:/";
|
return "redirect:/";
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> getFavColors(HttpSession session) {
|
private List<String> getFavColors(HttpSession session) {
|
||||||
List<String> favoriteColors = (List<String>) session.
|
List<String> favoriteColors = (List<String>) session.getAttribute("favoriteColors");
|
||||||
getAttribute("favoriteColors");
|
|
||||||
if (favoriteColors == null) {
|
if (favoriteColors == null) {
|
||||||
favoriteColors = new ArrayList<>();
|
favoriteColors = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,3 @@
|
|||||||
spring.session.store-type=jdbc
|
spring.session.store-type=jdbc
|
||||||
#spring.session.jdbc.initialize-schema=embedded
|
|
||||||
#spring.session.jdbc.table-name=SPRING_SESSION
|
|
||||||
#server.servlet.session.timeout=60s
|
|
||||||
#spring.datasource.url=jdbc:h2:mem:AZ
|
|
||||||
#spring.security.user.name=admin
|
|
||||||
#spring.security.user.password=secret
|
|
||||||
spring.h2.console.enabled=true
|
spring.h2.console.enabled=true
|
||||||
spring.h2.console.path=/h2-console
|
spring.h2.console.path=/h2-console
|
@ -1,17 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Spring Session JDBC</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div>
|
|
||||||
<form th:action="@{/saveColor}" method="post">
|
|
||||||
<input name="color" type="text" placeholder="Enter your favorite color"/>
|
|
||||||
<input type="submit" value="Save"/>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<p>Session ID - <span th:text="${sessionId}"/></p>
|
|
||||||
<p>My favorite color(s) - <span th:text="${favoriteColors}"/></p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,16 +0,0 @@
|
|||||||
package com.baeldung.springsessionjdbc;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
|
||||||
import org.springframework.test.context.junit4.SpringRunner;
|
|
||||||
|
|
||||||
@RunWith(SpringRunner.class)
|
|
||||||
@SpringBootTest
|
|
||||||
public class SpringSessionJdbcApplicationTests {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void contextLoads() {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,91 @@
|
|||||||
|
package com.baeldung.springsessionjdbc;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.FixMethodOrder;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.MethodSorters;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||||
|
import org.springframework.boot.web.server.LocalServerPort;
|
||||||
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
|
import org.springframework.util.MultiValueMap;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectInput;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.sql.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
@RunWith(SpringRunner.class)
|
||||||
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||||
|
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||||
|
public class SpringSessionJdbcIntegrationTest {
|
||||||
|
|
||||||
|
@LocalServerPort
|
||||||
|
private int port;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TestRestTemplate testRestTemplate;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() throws ClassNotFoundException {
|
||||||
|
Class.forName("org.h2.Driver");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenApiHasStarted_whenH2DbIsQueried_thenSessionTablesAreEmpty() throws SQLException {
|
||||||
|
Assert.assertEquals(0, getSessionIdsFromDatabase().size());
|
||||||
|
Assert.assertEquals(0, getSessionAttributeBytesFromDatabase().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenGetInvoked_whenH2DbIsQueried_thenOneSessionIsCreated() throws SQLException {
|
||||||
|
assertThat(this.testRestTemplate.getForObject("http://localhost:" + port + "/", String.class)).isNotEmpty();
|
||||||
|
Assert.assertEquals(1, getSessionIdsFromDatabase().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenPostInvoked_whenH2DbIsQueried_thenSessionAttributeIsRetrieved() throws ClassNotFoundException, SQLException, IOException {
|
||||||
|
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
|
||||||
|
map.add("color", "red");
|
||||||
|
this.testRestTemplate.postForObject("http://localhost:" + port + "/saveColor", map, String.class);
|
||||||
|
List<byte[]> queryResponse = getSessionAttributeBytesFromDatabase();
|
||||||
|
Assert.assertEquals(1, queryResponse.size());
|
||||||
|
ObjectInput in = new ObjectInputStream(new ByteArrayInputStream(queryResponse.get(0)));
|
||||||
|
List<String> obj = (List<String>) in.readObject(); //Deserialize byte[] to object
|
||||||
|
Assert.assertEquals("red", obj.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getSessionIdsFromDatabase() throws SQLException {
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
ResultSet rs = getResultSet("SELECT * FROM SPRING_SESSION");
|
||||||
|
while (rs.next()) {
|
||||||
|
result.add(rs.getString("SESSION_ID"));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<byte[]> getSessionAttributeBytesFromDatabase() throws SQLException {
|
||||||
|
List<byte[]> result = new ArrayList<>();
|
||||||
|
ResultSet rs = getResultSet("SELECT * FROM SPRING_SESSION_ATTRIBUTES");
|
||||||
|
while (rs.next()) {
|
||||||
|
result.add(rs.getBytes("ATTRIBUTE_BYTES"));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResultSet getResultSet(String sql) throws SQLException {
|
||||||
|
Connection conn = DriverManager.getConnection("jdbc:h2:mem:testdb", "sa", "");
|
||||||
|
Statement stat = conn.createStatement();
|
||||||
|
return stat.executeQuery(sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -37,6 +37,7 @@
|
|||||||
<groupId>com.baeldung</groupId>
|
<groupId>com.baeldung</groupId>
|
||||||
<artifactId>spring-swagger-codegen</artifactId>
|
<artifactId>spring-swagger-codegen</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<relativePath>../../spring-swagger-codegen</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
@ -7,9 +7,10 @@
|
|||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>com.baeldung</groupId>
|
||||||
<artifactId>spring-boot-starter-parent</artifactId>
|
<artifactId>spring-swagger-codegen</artifactId>
|
||||||
<version>1.5.10.RELEASE</version>
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<relativePath>../../spring-swagger-codegen</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@ -21,6 +22,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
<version>${spring.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
@ -36,6 +38,7 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<java.version>1.8</java.version>
|
<java.version>1.8</java.version>
|
||||||
<spring-swagger-codegen-api-client.version>0.0.1-SNAPSHOT</spring-swagger-codegen-api-client.version>
|
<spring-swagger-codegen-api-client.version>0.0.1-SNAPSHOT</spring-swagger-codegen-api-client.version>
|
||||||
|
<spring.version>1.5.10.RELEASE</spring.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
</project>
|
</project>
|
59
testing-modules/junit-abstract/pom.xml
Normal file
59
testing-modules/junit-abstract/pom.xml
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<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>
|
||||||
|
|
||||||
|
<artifactId>junit-abstract</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>abstractclasses</name>
|
||||||
|
<url>http://maven.apache.org</url>
|
||||||
|
<parent>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<artifactId>parent-modules</artifactId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
<relativePath>../../</relativePath>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<powermock.version>1.7.0</powermock.version>
|
||||||
|
<junit.version>4.12</junit.version>
|
||||||
|
<mockito.all.version>1.10.19</mockito.all.version>
|
||||||
|
<java.version>1.8</java.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-all</artifactId>
|
||||||
|
<version>${mockito.all.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.powermock</groupId>
|
||||||
|
<artifactId>powermock-module-junit4</artifactId>
|
||||||
|
<version>${powermock.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.powermock</groupId>
|
||||||
|
<artifactId>powermock-api-mockito</artifactId>
|
||||||
|
<version>${powermock.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<finalName>junit-abstract</finalName>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package org.baeldung.testing.abstractclass.abstractmethod;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When method calls abstract method.
|
||||||
|
*/
|
||||||
|
public abstract class AbstractMethodCalling {
|
||||||
|
|
||||||
|
public abstract String abstractFunc();
|
||||||
|
|
||||||
|
public String defaultImpl() {
|
||||||
|
String res = abstractFunc();
|
||||||
|
return (res == null) ? "Default" : (res + " Default");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package org.baeldung.testing.abstractclass.indepedentmethod;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test Independent Method
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public abstract class AbstractIndependent {
|
||||||
|
|
||||||
|
public abstract int abstractFunc();
|
||||||
|
|
||||||
|
public String defaultImpl() {
|
||||||
|
return "DEFAULT-1";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package org.baeldung.testing.abstractclass.indepedentmethod;
|
||||||
|
|
||||||
|
public class ConcreteImpl extends AbstractIndependent {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int abstractFunc() {
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package org.baeldung.testing.abstractclass.instancefields;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test Independent Method
|
||||||
|
*/
|
||||||
|
public abstract class AbstractInstanceFields {
|
||||||
|
|
||||||
|
protected int count;
|
||||||
|
private boolean active = false;
|
||||||
|
|
||||||
|
public abstract int abstractFunc();
|
||||||
|
|
||||||
|
public String testFunc() {
|
||||||
|
String response;
|
||||||
|
if (count > 5) {
|
||||||
|
response = "Overflow";
|
||||||
|
} else {
|
||||||
|
response = active ? "Added" : "Blocked";
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package org.baeldung.testing.abstractclass.privatemethod;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
public abstract class AbstractPrivateMethods {
|
||||||
|
|
||||||
|
public abstract int abstractFunc();
|
||||||
|
|
||||||
|
public String defaultImpl() {
|
||||||
|
return getCurrentDateTime() + "DEFAULT-1";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getCurrentDateTime() {
|
||||||
|
return LocalDateTime.now()
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package org.baeldung.testing.abstractclass.abstractmethod;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
|
public class AbstractMethodCallingUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenDefaultImpl_whenMockAbstractFunc_thenExpectedBehaviour() {
|
||||||
|
|
||||||
|
// mock classes and call real methods available
|
||||||
|
AbstractMethodCalling cls = Mockito.mock(AbstractMethodCalling.class);
|
||||||
|
Mockito.doReturn("Abstract")
|
||||||
|
.when(cls)
|
||||||
|
.abstractFunc();
|
||||||
|
Mockito.doCallRealMethod()
|
||||||
|
.when(cls)
|
||||||
|
.defaultImpl();
|
||||||
|
|
||||||
|
// validate result by mock abstractFunc's behaviour
|
||||||
|
assertEquals("Abstract Default", cls.defaultImpl());
|
||||||
|
|
||||||
|
// check the value with null response from abstract method
|
||||||
|
Mockito.doReturn(null)
|
||||||
|
.when(cls)
|
||||||
|
.abstractFunc();
|
||||||
|
assertEquals("Default", cls.defaultImpl());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package org.baeldung.testing.abstractclass.indepedentmethod;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
|
public class AbstractIndependentUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenNonAbstractMethod_whenConcreteImpl_testCorrectBehaviour() {
|
||||||
|
ConcreteImpl conClass = new ConcreteImpl();
|
||||||
|
String actual = conClass.defaultImpl();
|
||||||
|
|
||||||
|
assertEquals("DEFAULT-1", actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenNonAbstractMethod_whenMockitoMock_testCorrectBehaviour() {
|
||||||
|
AbstractIndependent absCls = Mockito.mock(AbstractIndependent.class, Mockito.CALLS_REAL_METHODS);
|
||||||
|
assertEquals("DEFAULT-1", absCls.defaultImpl());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
package org.baeldung.testing.abstractclass.instancefields;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.powermock.api.mockito.PowerMockito;
|
||||||
|
import org.powermock.reflect.Whitebox;
|
||||||
|
|
||||||
|
public class AbstractInstanceFieldsUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void protectedInstanceField_MockClassCountGt5_testNonAbstractMethod() {
|
||||||
|
|
||||||
|
// mock
|
||||||
|
AbstractInstanceFields instClass = Mockito.mock(AbstractInstanceFields.class);
|
||||||
|
Mockito.doCallRealMethod()
|
||||||
|
.when(instClass)
|
||||||
|
.testFunc();
|
||||||
|
|
||||||
|
// set counter greater than 5
|
||||||
|
instClass.count = 7;
|
||||||
|
|
||||||
|
// compare the result
|
||||||
|
assertEquals("Overflow", instClass.testFunc());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenNonAbstractMethodAndPrivateField_whenPowerMockitoAndActiveFieldTrue_thenCorrectBehaviour() {
|
||||||
|
AbstractInstanceFields instClass = PowerMockito.mock(AbstractInstanceFields.class);
|
||||||
|
PowerMockito.doCallRealMethod()
|
||||||
|
.when(instClass)
|
||||||
|
.testFunc();
|
||||||
|
Whitebox.setInternalState(instClass, "active", true);
|
||||||
|
|
||||||
|
// compare the expected result with actual
|
||||||
|
assertEquals("Added", instClass.testFunc());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package org.baeldung.testing.abstractclass.privatemethod;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.powermock.api.mockito.PowerMockito;
|
||||||
|
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||||
|
import org.powermock.modules.junit4.PowerMockRunner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Providing custom values for private methods using powermock
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@RunWith(PowerMockRunner.class)
|
||||||
|
@PrepareForTest(AbstractPrivateMethods.class)
|
||||||
|
public class AbstractPrivateMethodsUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenNonAbstractMethodAndCallPrivateMethod_whenMockPrivateMethod_thenVerifyBehaviour() throws Exception {
|
||||||
|
AbstractPrivateMethods mockClass = PowerMockito.mock(AbstractPrivateMethods.class);
|
||||||
|
PowerMockito.doCallRealMethod()
|
||||||
|
.when(mockClass)
|
||||||
|
.defaultImpl();
|
||||||
|
|
||||||
|
String dateTime = LocalDateTime.now()
|
||||||
|
.toString();
|
||||||
|
PowerMockito.doReturn(dateTime)
|
||||||
|
.when(mockClass, "getCurrentDateTime");
|
||||||
|
|
||||||
|
String actual = mockClass.defaultImpl();
|
||||||
|
assertEquals(dateTime + "DEFAULT-1", actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
2
testing-modules/spring-testing/README.md
Normal file
2
testing-modules/spring-testing/README.md
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
### Relevant Articles:
|
||||||
|
|
75
testing-modules/spring-testing/pom.xml
Normal file
75
testing-modules/spring-testing/pom.xml
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<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>org.baeldung</groupId>
|
||||||
|
<artifactId>spring-testing</artifactId>
|
||||||
|
<version>0.1-SNAPSHOT</version>
|
||||||
|
<name>spring-testing</name>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<artifactId>parent-java</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<relativePath>../../parent-java</relativePath>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<!-- test scoped -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.hamcrest</groupId>
|
||||||
|
<artifactId>java-hamcrest</artifactId>
|
||||||
|
<version>${hamcrest.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter</artifactId>
|
||||||
|
<version>LATEST</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<version>LATEST</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-core</artifactId>
|
||||||
|
<version>LATEST</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-context</artifactId>
|
||||||
|
<version>LATEST</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.persistence</groupId>
|
||||||
|
<artifactId>javax.persistence</artifactId>
|
||||||
|
<version>2.1.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.data</groupId>
|
||||||
|
<artifactId>spring-data-jpa</artifactId>
|
||||||
|
<version>LATEST</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<finalName>spring-testing</finalName>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/resources</directory>
|
||||||
|
<filtering>true</filtering>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<!-- testing -->
|
||||||
|
<hamcrest.version>2.0.0.0</hamcrest.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
</project>
|
@ -20,7 +20,7 @@ public class MockBeanAnnotationIntegrationTest {
|
|||||||
ApplicationContext context;
|
ApplicationContext context;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenCountMethodMocked_WhenCountInvokedOnBeanFromContext_ThenMockValueReturned() {
|
public void givenCountMethodMocked_WhenCountInvoked_ThenMockValueReturned() {
|
||||||
Mockito.when(mockRepository.count()).thenReturn(123L);
|
Mockito.when(mockRepository.count()).thenReturn(123L);
|
||||||
|
|
||||||
UserRepository userRepoFromContext = context.getBean(UserRepository.class);
|
UserRepository userRepoFromContext = context.getBean(UserRepository.class);
|
@ -1,10 +0,0 @@
|
|||||||
<?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>
|
|
@ -1,32 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<tutorials>
|
|
||||||
<tutorial tutId="01" type="java">
|
|
||||||
<title>Guava updated</title>
|
|
||||||
<description>Introduction to Guava</description>
|
|
||||||
<date>04/04/2016</date>
|
|
||||||
<author>GuavaAuthor</author>
|
|
||||||
</tutorial>
|
|
||||||
<tutorial tutId="02" type="java">
|
|
||||||
<title>XML updated</title>
|
|
||||||
<description>Introduction to XPath</description>
|
|
||||||
<date>04/05/2016</date>
|
|
||||||
<author>XMLAuthor</author>
|
|
||||||
</tutorial>
|
|
||||||
<tutorial tutId="03" type="android">
|
|
||||||
<title>Android updated</title>
|
|
||||||
<description>Introduction to Android</description>
|
|
||||||
<date>04/03/2016</date>
|
|
||||||
<author>AndroidAuthor</author>
|
|
||||||
</tutorial>
|
|
||||||
<tutorial tutId="04" type="java">
|
|
||||||
<title>Spring updated</title>
|
|
||||||
<description>Introduction to Spring</description>
|
|
||||||
<date>04/02/2016</date>
|
|
||||||
<author>SpringAuthor</author>
|
|
||||||
<sections>
|
|
||||||
<section name="core">Spring Core</section>
|
|
||||||
<section name="mvc">Spring MVC</section>
|
|
||||||
<section name="batch">Spring Batch</section>
|
|
||||||
</sections>
|
|
||||||
</tutorial>
|
|
||||||
</tutorials>
|
|
@ -1,9 +0,0 @@
|
|||||||
<?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…
x
Reference in New Issue
Block a user