Pages

Footer Pages

Spring Boot

Java String API

Java Conversions

Kotlin Programs

Kotlin Conversions

Java Threads Tutorial

Java 8 Tutorial

Monday, January 6, 2020

Creating A Simple ToDo Spring Web Application (Spring MVC)

1. Overview


In this tutorial, We'll learn how to create a simple ToDo web application using the Spring web framework. Spring web framework is called as officially Spring MVC. Let us create a simple REST api with CRUD operations such as Create, Read, Update, and Delete. You will learn and understand by a step by step tutorial. In the previous article, we have shown how to build a first Hello World application in Spring Boot and all possible real-time exceptions.

Spring 5.0.3.RELEASE
Bootstrap 3.3.7
Hibernate 4.3.11.Final
h2.version 1.4.197
JDK 1.8
Spring data 1.11.4.RELEASE





2. Create a Maven Web Application


First, We have to create a simple web application using maven command.

mvn archetype:generate -DgroupId=com.javaprogramto.todo -DartifactId=todo -Dversion=0.0.1-SNAPSHOT -DinteractiveMode=false -DarchetypeArtifactId=maven-archetype-webapp

This command generates a very basic web application structure. Under folder src/main only two directories will be created such as resources and webapp.
Folder java will not be created. You need to create it manually.

Note: After executing this command, you must see the pom.xml file in the project root folder.

3. Adding dependencies to pom.xml


Let us add all the dependencies that are required for this project.

<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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.javaprogramto.todo</groupId>
    <artifactId>todo</artifactId>
    <packaging>war</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <name>todo Webapp</name>
    <properties>
        <!-- Generic properties -->
        <java.version>1.8</java.version>
        <!-- Web -->
        <jsp.version>2.2</jsp.version>
        <jstl.version>1.2</jstl.version>
        <servlet.version>3.1.0</servlet.version>
        <bootstrap.version>3.3.7</bootstrap.version>
        <jackson.version>2.9.2</jackson.version>
        <webjars.version>0.32</webjars.version>
        <!-- Spring -->
        <spring-framework.version>
            5.0.3.RELEASE
        </spring-framework.version>
        <!-- JPA -->
        <spring-data-jpa>1.11.4.RELEASE</spring-data-jpa>
        <hibernate-jpa.version>1.0.0.Final</hibernate-jpa.version>
        <hibernate.version>4.3.11.Final</hibernate.version>
        <!-- Drivers -->
        <h2.version>1.4.197</h2.version>
        <!-- Logs -->
        <slf4j.version>1.7.25</slf4j.version>
        <logback.version>1.2.3</logback.version>
    </properties>
    <dependencies>
        <!-- Spring MVC -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring-framework.version}</version>
        </dependency>
        <!-- Spring Data JPA -->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>${spring-data-jpa}</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate.javax.persistence</groupId>
            <artifactId>hibernate-jpa-2.1-api</artifactId>
            <version>${hibernate-jpa.version}</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>${hibernate.version}</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>${hibernate.version}</version>
        </dependency>
        <!-- Logs -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>${logback.version}</version>
        </dependency>
        <!-- Drivers -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>${h2.version}</version>
            <scope>runtime</scope>
        </dependency>
        <!-- Java EE Web dependencies -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>${jstl.version}</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>${servlet.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>${jsp.version}</version>
            <scope>provided</scope>
        </dependency>
        <!-- Web UI -->
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>webjars-locator</artifactId>
            <version>${webjars.version}</version>
        </dependency>
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>bootstrap</artifactId>
            <version>${bootstrap.version}</version>
        </dependency>
        <!-- Web - JSON/XML Response -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>${jackson.version}</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-joda</artifactId>
            <version>${jackson.version}</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
            <version>${jackson.version}</version>
        </dependency>
    </dependencies>
    <build>
        <finalName>todo</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

4. Web.xml Configuration


Adding DispatcherServlet in web.xml and this is the main entry point for this web app that acts as front controller to handles all incoming requests.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    version="2.5">

    <display-name>ToDo Web Application</display-name>
    <servlet>
        <servlet-name>todoDispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>todoDispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

5. Spring Configuration File


Spring DispatcherServlet will look for the configuration file with the name "todoDispatcherServlet-servlet.xml" in the WEB-INF folder. This name is a combination of "servlet-name" and '-servlet'.

Configure the following beans which are needed for rest and web application.

todoDispatcherServlet-servlet.xml:

<context:component-scan base-package="com.javaprogramto.todo" />
    <mvc:annotation-driven>
        <mvc:message-converters>
            <bean
                class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="objectMapper" ref="jsonMapper" />
            </bean>
            <bean
                class="org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter">
                <property name="objectMapper" ref="xmlMapper" />
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>
    <bean id="jsonMapper" class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
        <property name="simpleDateFormat" value="yyyy-MM-dd HH:mm:ss" />
    </bean>
    <bean id="xmlMapper" parent="jsonMapper">
        <property name="createXmlMapper" value="true" />
    </bean>
    <mvc:resources mapping="/webjars/**"
        location="classpath:META-INF/resources/webjars/" />
    <jpa:repositories base-package="com.javaprogramto.todo.repository" />
    <jdbc:embedded-database id="dataSource"
        type="H2">
        <jdbc:script location="classpath:META-INF/sql/schema.sql" />
        <jdbc:script location="classpath:META-INF/sql/data.sql" />
    </jdbc:embedded-database>
    <bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
        <property name="showSql" value="true" />
    </bean>
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
    </bean>
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory"
            ref="entityManagerFactory" />
    </bean>
    <tx:annotation-driven
        transaction-manager="transactionManager" />
    <bean
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/" />
        <property name="suffix" value=".jsp" />
    </bean>
    <bean id="h2WebServer" class="org.h2.tools.Server"     factory-method="createWebServer" init-method="start"
        destroy-method="stop">
        <constructor-arg
            value="-web,-webAllowOthers,-webDaemon,-webPort,8082" />
    </bean>

 
<context:component-scan/> is to detect the all classes that annotated with @Configuration and @Service.
<mvc:annotation-driven/> is to detect the classes with @Controller and @RESTController.
<jpa:repositories/> is to find the interfaces that extends CrudRepository interfaces

6. SQL Scripts and JPA Repository - Persistance Unit


We are going to use table creation and data insertion scripts in our example.

6.1 schema.sql:


create table todo (
    id varchar(36) not null,
    description varchar(255) not null,
    created timestamp,
    modified timestamp,
    completed boolean,
    primary key (id)
);

6.2 data.sql 



insert into todo values ('7fd921cfd2b64dc7b995633e8209f386','Pay House Rent','2019-09-23 15:00:01','2019-09-23 15:00:01',false);
insert into todo values ('5820a4c2abe74f409da89217bf905a0s','Study Documents','2019-09-02 16:00:01','2019-09-02 16:00:01',false);
insert into todo values ('a44b6db26aef49e39d1b68622f55c34j','Doctor Consultation','2019-09-18 12:00:00','2019-09-18 12:00:00',false);

6.3 /resources/META-INF/persistence.xml


Create a persistence unit in the persistence.xml file as below.


<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
    version="1.0">
    <persistence-unit name="toDo">
        <description>My Persistence Unit</description>
    </persistence-unit>
</persistence>


Spring DAO Library will handle all the required dependencies for connection and its mapping classes. We need to just declare only <persistence-unit> name.

Note: Once you complete all these configurations, please run "maven install" project in eclipse or through the command line.
Do maven -> "update project" to update the dependencies for this project.

7. Create ToDo Application classes


As of now, we have seen all the configurations required for this app. Let us create java classes.

7.1 ToDo Entity Class


This class interacts and maps to the actual database table "todo".

package com.javaprogramto.todo.domain;

import java.sql.Timestamp;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

import org.hibernate.annotations.GenericGenerator;

@Entity
public class ToDo {
    @Id
    @GeneratedValue(generator = "system-uuid")
    @GenericGenerator(name = "system-uuid", strategy = "uuid")
    private String id;
    private String description;
    private Timestamp created;
    private Timestamp modified;
    private boolean completed;

    public ToDo() {
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Timestamp getCreated() {
        return created;
    }

    public void setCreated(Timestamp created) {
        this.created = created;
    }

    public Timestamp getModified() {
        return modified;
    }

    public void setModified(Timestamp modified) {
        this.modified = modified;
    }

    public boolean isCompleted() {
        return completed;
    }
}

@Entity is to tell spring DAO that this is the mapping to the table.
@Id is to indicate this property as Primary Key
@GeneratedValue(generator = "system-uuid") is to generate 36 character random guid code.

7.2 Creating Repository


Create an interface that extends the CrudRepository interface which takes Entity class and primary key Id type. In our case, entity class is ToDo and the primary key type is String.


package com.javaprogramto.todo.repository;

import org.springframework.data.repository.CrudRepository;

import com.javaprogramto.todo.domain.ToDo;

public interface ToDoRepository extends CrudRepository<ToDo, String> {

}

This class is scanned and identified by <jpa:repositories/> tag in todoDispatcherServlet-servlet.xml file. Because this tag is pointing to package "com.javaprogramto.todo.repository".

7.3 Creating Controller Class


This class is annotated with @Controller which is indentified by &lt;mv:annotation-driven/&gt; tag. Maps @GetMapping, @RequestMapping, and @PostMapping annotations to accept requests for paths / and /toDos.

This controller class needs a ToDoRepository so it should be wired up properly. Spring provides another annotation @Autowired which does wiring up the dependency by spring framework. Dependency is injected by the spring container.

package com.javaprogramto.todo.controller;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import com.javaprogramto.todo.domain.ToDo;
import com.javaprogramto.todo.repository.ToDoRepository;

@Controller
@RequestMapping("/")
public class ToDoController {
    private ToDoRepository repository;

    @Autowired
    public ToDoController(ToDoRepository repository) {
        this.repository = repository;
    }

    @GetMapping
    public ModelAndView index(ModelAndView modelAndView, HttpServletRequest request) {
        modelAndView.setViewName("index");
        return modelAndView;
    }

    @RequestMapping(value = "/toDos", method = { RequestMethod.GET }, produces = {
            MediaType.APPLICATION_JSON_UTF8_VALUE, MediaType.APPLICATION_XML_VALUE, MediaType.TEXT_XML_VALUE })
    public ResponseEntity<Iterable<ToDo>> getToDos(@RequestHeader HttpHeaders headers) {
        return new ResponseEntity<Iterable<ToDo>>(this.repository.findAll(), headers, HttpStatus.OK);
    }
}

GetMapping request to path "/" will bring the index.jsp page.
GetMapping request to path "/toDos" will fetch all ToDo records from the database using the repository.findAll() method and returns the response as JSON.

7.4 Home page index.jsp file


Let us create a simple home page when URL "/" is hit.

src/main/webapp/WEB-INF/views/index.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Simple Directory Web App</title>
<link rel="stylesheet" type="text/css"
    href="webjars/bootstrap/3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css"
    href="webjars/bootstrap/3.3.7/css/bootstrap-theme.min.css">
</head>
<body>
    <div class="container theme-showcase" role="main">
        <div class="jumbotron">
            <h1>ToDo Application</h1>
            <p>A simple Rest API Spring MVC application</p>
        </div>
        <div class="page-header">
            <h1>API</h1>
            <a href="toDos">Current ToDos</a>
        </div>
    </div>
</body>
</html>

In this jsp file, we have mentioned bootstrap.min.css. You might have a question and where it is coming from?
We have already declared webjars:bootstrap dependency in pom.xml and <mvc:resources/> tag in dispatcherServlet-servlet.xml file. These two will make bootstrap library files available to this application.

8. Build and Deploy the app in Tomcat Server


Let us build the app using the maven install command which generates the todo-0.0.1-SNAPSHOT.war file.

Copy the generated war file into tomcat/webapps folder. Next, run the startup.bat for windows and startup.sh for UNIX/mac.

Hit http://localhost:8080/todo/ from your favorite browser. This will display the index.jsp file.


todo_homepage



localhost:8080/todo/toDos

This request will be sent to the toDos mapping in the controller and fetches all three records from DB and shows on the browser.

getMapping_toDos

<ArrayList>
        <item>
            <id>7fd921cfd2b64dc7b995633e8209f386</id>
            <description>Pay House Rent</description>
            <created>2019-09-23 15:00:01</created>
            <modified>2019-09-23 15:00:01</modified>
            <completed>false</completed>
        </item>
        <item>
            <id>5820a4c2abe74f409da89217bf905a0s</id>
            <description>Study Documents</description>
            <created>2019-09-02 16:00:01</created>
            <modified>2019-09-02 16:00:01</modified>
            <completed>false</completed>
        </item>
        <item>
            <id>a44b6db26aef49e39d1b68622f55c34j</id>
            <description>Doctor Consultation</description>
            <created>2019-09-18 12:00:00</created>
            <modified>2019-09-18 12:00:00</modified>
            <completed>false</completed>
        </item>
    </ArrayList>


The response can be viewed in json using the curl command.

curl -H "Accept: application/json" localhost:8080/todo/toDos

9. Using App Config


Some of the developers may think using XML configuration is verbose and not convenient. We can configure in another way using annotations and java-config classes.

All the tags inside todoDispatcherServlet-servlet.xml can be moved to java file as below.

package com.javaprogramto.todo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.resource.WebJarsResourceResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import javax.sql.DataSource;
import java.text.SimpleDateFormat;
import java.util.List;

@Configuration
@EnableJpaRepositories(basePackages = "com.apress.todo.repository")
@EnableTransactionManagement
@EnableWebMvc
public class ToDoConfig implements WebMvcConfigurer {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/", "/resources/", "/webjars/")
                .resourceChain(true).addResolver(new WebJarsResourceResolver());
    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
        builder.indentOutput(true).dateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
        converters.add(new MappingJackson2XmlHttpMessageConverter(builder.createXmlMapper(true).build()));
    }

    @Bean
    public InternalResourceViewResolver jspViewResolver() {
        InternalResourceViewResolver bean = new InternalResourceViewResolver();
        bean.setPrefix("/WEB-INF/views/");
        bean.setSuffix(".jsp");
        return bean;
    }

    @Bean
    public DataSource dataSource() {
        EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
        return builder.setType(EmbeddedDatabaseType.H2).addScript("META-INF/sql/schema.sql")
                .addScript("META-INF/sql/data.sql").build();
    }

    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        vendorAdapter.setShowSql(true);
        LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
        factory.setJpaVendorAdapter(vendorAdapter);
        factory.setDataSource(dataSource());
        return factory;
    }

    @Bean
    public PlatformTransactionManager transactionManager() {
        JpaTransactionManager txManager = new JpaTransactionManager();
        txManager.setEntityManagerFactory(entityManagerFactory().getNativeEntityManagerFactory());
        return txManager;
    }
}

and remove all tags in the todoDispatcherServlet-servlet.xml file and add only below tag.

<context:component-scan base-package="com.apress.todo" />

10. Spring Exceptions and Solutions


The following exceptions have occurred during the development of this ToDo application.

10.1 Verify the dependency is in pom.xml file:


dependency name: spring-webmvc


Jan 06, 2020 3:41:19 PM org.apache.catalina.core.StandardContext loadOnStartup
SEVERE: Servlet [todoDispatcherServlet] in web application [/todo] threw load() exception
java.lang.ClassNotFoundException: org.springframework.web.servlet.DispatcherServlet
    at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1365)
    at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1188)
    at org.apache.catalina.core.DefaultInstanceManager.loadClass(DefaultInstanceManager.java:540)
    at org.apache.catalina.core.DefaultInstanceManager.loadClassMaybePrivileged(DefaultInstanceManager.java:521)
    at org.apache.catalina.core.DefaultInstanceManager.newInstance(DefaultInstanceManager.java:150)
    at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1042)
    at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:983)
    at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4871)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5180)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1384)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1374)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
    at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134)
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:909)
    at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:841)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1384)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1374)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
    at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134)
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:909)
    at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:262)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
    at org.apache.catalina.core.StandardService.startInternal(StandardService.java:421)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
    at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:930)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:633)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:343)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:474)

10.2 If servlet name and mappings are not in matching:


SEVERE: A child container failed during start
java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/todo]]
    at java.util.concurrent.FutureTask.report(FutureTask.java:122)
    at java.util.concurrent.FutureTask.get(FutureTask.java:192)
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:916)
    at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:841)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1384)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1374)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
    at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134)
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:909)
    at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:262)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
    at org.apache.catalina.core.StandardService.startInternal(StandardService.java:421)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
    at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:930)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:633)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:343)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:474)
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/todo]]
    at org.apache.catalina.util.LifecycleBase.handleSubClassException(LifecycleBase.java:440)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:198)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1384)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1374)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
    at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134)
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:909)
    ... 21 more
Caused by: java.lang.IllegalArgumentException: Servlet mapping specifies an unknown servlet name [dispatcherServlet]
    at org.apache.catalina.core.StandardContext.addServletMappingDecoded(StandardContext.java:3174)
    at org.apache.catalina.Context.addServletMappingDecoded(Context.java:881)
    at org.apache.catalina.startup.ContextConfig.configureContext(ContextConfig.java:1391)
    at org.apache.catalina.startup.ContextConfig.webConfig(ContextConfig.java:1168)
    at org.apache.catalina.startup.ContextConfig.configureStart(ContextConfig.java:774)
    at org.apache.catalina.startup.ContextConfig.lifecycleEvent(ContextConfig.java:301)
    at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:123)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5051)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
    ... 27 more

Jan 06, 2020 3:36:57 PM org.apache.catalina.core.ContainerBase startInternal
SEVERE: A child container failed during start
java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: A child container failed during start
    at java.util.concurrent.FutureTask.report(FutureTask.java:122)
    at java.util.concurrent.FutureTask.get(FutureTask.java:192)
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:916)
    at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:262)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
    at org.apache.catalina.core.StandardService.startInternal(StandardService.java:421)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
    at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:930)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:633)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:343)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:474)
Caused by: org.apache.catalina.LifecycleException: A child container failed during start
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:928)
    at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:841)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1384)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1374)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
    at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134)
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:909)
    ... 13 more
Caused by: java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/todo]]
    at java.util.concurrent.FutureTask.report(FutureTask.java:122)
    at java.util.concurrent.FutureTask.get(FutureTask.java:192)
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:916)
    ... 21 more
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/todo]]
    at org.apache.catalina.util.LifecycleBase.handleSubClassException(LifecycleBase.java:440)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:198)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1384)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1374)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
    at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134)
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:909)
    ... 21 more
Caused by: java.lang.IllegalArgumentException: Servlet mapping specifies an unknown servlet name [dispatcherServlet]
    at org.apache.catalina.core.StandardContext.addServletMappingDecoded(StandardContext.java:3174)
    at org.apache.catalina.Context.addServletMappingDecoded(Context.java:881)
    at org.apache.catalina.startup.ContextConfig.configureContext(ContextConfig.java:1391)
    at org.apache.catalina.startup.ContextConfig.webConfig(ContextConfig.java:1168)
    at org.apache.catalina.startup.ContextConfig.configureStart(ContextConfig.java:774)
    at org.apache.catalina.startup.ContextConfig.lifecycleEvent(ContextConfig.java:301)
    at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:123)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5051)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
    ... 27 more

Jan 06, 2020 3:36:57 PM org.apache.catalina.startup.Catalina start
SEVERE: The required Server component failed to start so Tomcat is unable to start.
org.apache.catalina.LifecycleException: A child container failed during start
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:928)
    at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:262)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
    at org.apache.catalina.core.StandardService.startInternal(StandardService.java:421)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
    at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:930)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:633)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:343)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:474)
Caused by: java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: A child container failed during start
    at java.util.concurrent.FutureTask.report(FutureTask.java:122)
    at java.util.concurrent.FutureTask.get(FutureTask.java:192)
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:916)
    ... 13 more
Caused by: org.apache.catalina.LifecycleException: A child container failed during start
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:928)
    at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:841)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1384)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1374)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
    at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134)
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:909)
    ... 13 more
Caused by: java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/todo]]
    at java.util.concurrent.FutureTask.report(FutureTask.java:122)
    at java.util.concurrent.FutureTask.get(FutureTask.java:192)
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:916)
    ... 21 more
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/todo]]
    at org.apache.catalina.util.LifecycleBase.handleSubClassException(LifecycleBase.java:440)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:198)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1384)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1374)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
    at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134)
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:909)
    ... 21 more
Caused by: java.lang.IllegalArgumentException: Servlet mapping specifies an unknown servlet name [dispatcherServlet]
    at org.apache.catalina.core.StandardContext.addServletMappingDecoded(StandardContext.java:3174)
    at org.apache.catalina.Context.addServletMappingDecoded(Context.java:881)
    at org.apache.catalina.startup.ContextConfig.configureContext(ContextConfig.java:1391)
    at org.apache.catalina.startup.ContextConfig.webConfig(ContextConfig.java:1168)
    at org.apache.catalina.startup.ContextConfig.configureStart(ContextConfig.java:774)
    at org.apache.catalina.startup.ContextConfig.lifecycleEvent(ContextConfig.java:301)
    at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:123)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5051)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
    ... 27 more

Jan 06, 2020 3:36:57 PM org.apache.coyote.AbstractProtocol pause
INFO: Pausing ProtocolHandler ["http-nio-8080"]
Jan 06, 2020 3:36:57 PM org.apache.coyote.AbstractProtocol pause
INFO: Pausing ProtocolHandler ["ajp-nio-8009"]
Jan 06, 2020 3:36:57 PM org.apache.catalina.core.StandardService stopInternal
INFO: Stopping service [Catalina]
Jan 06, 2020 3:36:57 PM org.apache.coyote.AbstractProtocol destroy
INFO: Destroying ProtocolHandler ["http-nio-8080"]
Jan 06, 2020 3:36:57 PM org.apache.coyote.AbstractProtocol destroy
INFO: Destroying ProtocolHandler ["ajp-nio-8009"]

10.3 If persistence.xml file is missing:


2020-01-06 16:05:05 ERROR o.s.web.servlet.DispatcherServlet - Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in ServletContext resource [/WEB-INF/todoDispatcherServlet-servlet.xml]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: No persistence units parsed from {classpath*:META-INF/persistence.xml}
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1710)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:583)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:502)
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:312)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:310)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
        at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1085)
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:858)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549)
        at org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:676)
        at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:642)
        at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:690)
        at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:558)
        at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:499)
        at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:172)
        at javax.servlet.GenericServlet.init(GenericServlet.java:158)
        at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1134)
        at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1089)
        at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:983)
        at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4871)
        at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5180)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
        at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:717)
        at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:690)
        at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:705)
        at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:978)
        at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1849)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
        at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
        at org.apache.catalina.startup.HostConfig.deployWARs(HostConfig.java:773)
        at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:427)
        at org.apache.catalina.startup.HostConfig.start(HostConfig.java:1576)
        at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:309)
        at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:123)
        at org.apache.catalina.util.LifecycleBase.setStateInternal(LifecycleBase.java:423)
        at org.apache.catalina.util.LifecycleBase.setState(LifecycleBase.java:366)
        at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:936)
        at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:841)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
        at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1384)
        at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1374)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
        at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134)
        at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:909)
        at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:262)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
        at org.apache.catalina.core.StandardService.startInternal(StandardService.java:421)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
        at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:930)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
        at org.apache.catalina.startup.Catalina.start(Catalina.java:633)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:343)
        at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:474)
Caused by: java.lang.IllegalStateException: No persistence units parsed from {classpath*:META-INF/persistence.xml}
        at org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager.obtainDefaultPersistenceUnitInfo(DefaultPersistenceUnitManager.java:680)
        at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.determinePersistenceUnitInfo(LocalContainerEntityManagerFactoryBean.java:375)
        at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:329)
        at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:370)
        at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:359)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1769)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1706)
        ... 60 common frames omitted


11. Conclusion


In this article, We've seen how to build a ToDo application in Spring. And also shown replacing all XML configurations with java config classes and annotations.

Finally, showcased the errors found during the development of this app.

In the next article, we will build the same ToDo web application with Spring Boot.



No comments:

Post a Comment

Please do not add any spam links in the comments section.