14 September, 2011

CRUD RESTful services using Spring Framework

In this post I will show how to build CRUD (Create, Read, Update, Delete) REST-ful (Representational State Transfer) services using Spring Framework. Spring Framework version used in this example is 3.0.6.RELEASE. Apache Tomcat version used for testing the web services is 6.0.29 (default Tomcat version used by Tomcat Maven Plugin of Apache Maven 2.2.1). Project sources are available for download. So let’s start, step by step:

  1. First we have to define the resource on which the CRUD operation will be performed and how this resource will be represented. In our example we will use a resource that represents a User. The resource will be represented using XML (Extensible Markup Language). At the end of the post I will show how you can switch the resource representation to JSON (JavaScript Object Notation) or any other supported form.
  2. Create a Maven project. We will use Maven for project management and build. Maven POM is shown below:
    <?xml version="1.0"?>
    <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
              http://maven.apache.org/xsd/maven-4.0.0.xsd"
        xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <modelVersion>4.0.0</modelVersion>
        <groupId>org.zmeu</groupId>
        <artifactId>zmeu-blog-spring-rest</artifactId>
        <version>1.0.0-SNAPSHOT</version>
        <packaging>war</packaging>
        <name>ZMEU Blog Spring REST</name>
        <build>
            <pluginManagement>
                <plugins>
                    <plugin>
                        <artifactId>maven-compiler-plugin</artifactId>
                        <configuration>
                            <source>1.6</source>
                            <target>1.6</target>
                        </configuration>
                    </plugin>
                </plugins>
            </pluginManagement>
        </build>
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-web</artifactId>
                <version>3.0.6.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>3.0.6.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
                <version>3.0</version>
            </dependency>
            <!-- Logging dependencies -->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>1.6.1</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-log4j12</artifactId>
                <version>1.6.1</version>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.16</version>
                <scope>runtime</scope>
            </dependency>
            <!-- Testing dependencies -->
            <dependency>
                <groupId>org.testng</groupId>
                <artifactId>testng</artifactId>
                <version>6.2</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </project>
    
  3. Write User class that will represent our resource. Due to fact that we have decided to represent our resource using XML, we will annotate our class using JAXB (Java Architecture for XML Binding) annotations. When JAXB is used, Spring requires java classes to be annotated with @XmlRootElement (see Jaxb2RootElementHttpMessageConverter). If you are using Java 6 then you don’t have to do anything special because Java 6 is coming already with JAXB2, otherwise you will have to include dependencies to JAXB into your project. You can see below the User.java listing:
    package org.zmeu.blog.spring.rest.domain;
    
    import java.util.Date;
    
    import javax.xml.bind.annotation.XmlAccessType;
    import javax.xml.bind.annotation.XmlAccessorType;
    import javax.xml.bind.annotation.XmlRootElement;
    import javax.xml.bind.annotation.XmlType;
    
    import org.apache.commons.lang3.builder.EqualsBuilder;
    import org.apache.commons.lang3.builder.HashCodeBuilder;
    import org.apache.commons.lang3.builder.ToStringBuilder;
    
    @XmlAccessorType(XmlAccessType.PROPERTY)
    @XmlType(name = "userType")
    @XmlRootElement(name = "user")
    public class User {
        private long id;
        private String name;
        private Date registrationDate;
    
        public long getId() {
            return id;
        }
    
        public void setId(long id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Date getRegistrationDate() {
            return registrationDate;
        }
    
        public void setRegistrationDate(Date registrationDate) {
            this.registrationDate = registrationDate;
        }
    
        @Override
        public String toString() {
            ToStringBuilder builder = new ToStringBuilder(this);
            builder.append("id", getId());
            builder.append("name", getName());
            builder.append("registrationDate", getRegistrationDate());
            return builder.toString();
        }
    
        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }
            if (obj == null) {
                return false;
            }
            if (!(obj instanceof User)) {
                return false;
            }
            User other = (User) obj;
            EqualsBuilder equalsBuilder = new EqualsBuilder();
            equalsBuilder.append(getId(), other.getId());
            return equalsBuilder.isEquals();
        }
        
        @Override
        public int hashCode() {
            HashCodeBuilder hashCodeBuilder = new HashCodeBuilder();
            hashCodeBuilder.append(getId());
            return hashCodeBuilder.toHashCode();
        }
    }
    
  4. In order to represent a list of users we will create a new class UserList.java
    package org.zmeu.blog.spring.rest.domain;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.xml.bind.annotation.XmlAccessType;
    import javax.xml.bind.annotation.XmlAccessorType;
    import javax.xml.bind.annotation.XmlElement;
    import javax.xml.bind.annotation.XmlRootElement;
    import javax.xml.bind.annotation.XmlType;
    
    @XmlAccessorType(XmlAccessType.PROPERTY)
    @XmlType(name = "userListType")
    @XmlRootElement(name = "userList")
    public class UserList {
        private List<User> users = new ArrayList<User>();
    
        public UserList() {}
    
        public UserList(List<User> users) {
            this.users = users;
        }
    
        @XmlElement(name = "user")
        public List<User> getUsers() {
            return users;
        }
    
        public void setUsers(List<User> users) {
            this.users = users;
        }
    }
    
  5. Write a Spring service that will provide CRUD operations for our resource. In our case this is a simple service which uses a java.util.Map for storage. Normally this should be a service that performs some validations and delegates the persistence activities to a DAO (Data Access Object). First define the interface UserService.java:
    package org.zmeu.blog.spring.rest.service;
    
    import java.util.Collection;
    
    import org.zmeu.blog.spring.rest.domain.User;
    
    public interface UserService {
        User create(User user);
    
        User read(long userId);
    
        User update(User user);
    
        User delete(long userId);
    
        Collection<User> list();
    }
    
    And after the implementation UserServiceImpl.java:
    package org.zmeu.blog.spring.rest.service;
    
    import java.util.Collection;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.ConcurrentMap;
    import java.util.concurrent.atomic.AtomicLong;
    
    import org.apache.commons.lang3.Validate;
    import org.springframework.stereotype.Service;
    import org.zmeu.blog.spring.rest.domain.User;
    
    @Service
    public class UserServiceImpl implements UserService {
        private final AtomicLong USER_ID_SEQ = new AtomicLong();
        private final ConcurrentMap<Long, User> usersMap =
            new ConcurrentHashMap<Long, User>();
        
        @Override
        public User create(User user) {
            user.setId(USER_ID_SEQ.incrementAndGet());
            usersMap.put(user.getId(), user);
            return user;
        }
    
        @Override
        public User read(long userId) {
            return usersMap.get(userId);
        }
    
        @Override
        public User update(User user) {
            User updatedUser = usersMap.replace(user.getId(), user);
            Validate.isTrue(updatedUser != null,
                "Unable to find user with id: " + user.getId());
            return updatedUser;
        }
    
        @Override
        public User delete(long userId) {
            User removedUser = usersMap.remove(userId);
            Validate.isTrue(removedUser != null,
                "Unable to find user with id: " + userId);
            return removedUser;
        }
    
        @Override
        public Collection<User> list() {
            return usersMap.values();
        }
    }
    
  6. Write a Spring MVC controller that will do the actual work required by a RESTful service. Our controller maps to the following locations:
    URL HTTP Method Description
    http://host:port/servletContextPath/users GET Retrieves the user list.
    http://host:port/servletContextPath/users POST Creates a new user.
    http://host:port/servletContextPath/users/12 GET Retrieves user with id = 12.
    http://host:port/servletContextPath/users/12 PUT Updates user with id = 12.
    http://host:port/servletContextPath/users/12 DELETE Deletes user with id = 12.

    Below you can see UserController.java source code:
    package org.zmeu.blog.spring.rest.controller;
    
    import java.util.ArrayList;
    
    import org.apache.commons.lang3.Validate;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.HttpStatus;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.bind.annotation.ResponseStatus;
    import org.zmeu.blog.spring.rest.domain.User;
    import org.zmeu.blog.spring.rest.domain.UserList;
    import org.zmeu.blog.spring.rest.service.UserService;
    
    @Controller
    @RequestMapping(value = "/users")
    public class UserController {
        private static final Logger LOGGER = LoggerFactory.getLogger(
            UserController.class);
    
        @Autowired
        private UserService userService;
    
        @RequestMapping(method = RequestMethod.POST)
        @ResponseBody
        public User create(@RequestBody User user) {
            LOGGER.info("Creating new user {}", user);
            return userService.create(user);
        }
    
        @RequestMapping(value = "/{userId}", method = RequestMethod.GET)
        @ResponseBody
        public User read(@PathVariable(value = "userId") long userId) {
            LOGGER.info("Reading user with id {}", userId);
            User user = userService.read(userId);
            Validate.isTrue(user != null, "Unable to find user with id: " + userId);
            return user;
        }
    
        @RequestMapping(value = "/{userId}", method = RequestMethod.PUT)
        @ResponseStatus(value = HttpStatus.NO_CONTENT)
        public void update(@PathVariable(value = "userId") long userId,
            @RequestBody User user) {
            LOGGER.info("Updating user with id {} with {}", userId, user);
            Validate.isTrue(userId == user.getId(),
                "userId doesn't match URL userId: " + user.getId());
            userService.update(user);
        }
    
        @RequestMapping(value = "/{userId}", method = RequestMethod.DELETE)
        @ResponseStatus(value = HttpStatus.NO_CONTENT)
        public void delete(@PathVariable(value = "userId") long userId) {
            LOGGER.info("Deleting user with id {}", userId);
            userService.delete(userId);
        }
    
        @RequestMapping(method = RequestMethod.GET)
        @ResponseBody
        public UserList list() {
            LOGGER.info("Listing users");
            return new UserList(new ArrayList<User>(userService.list()));
        }
    
        @ExceptionHandler(IllegalArgumentException.class)
        @ResponseStatus(HttpStatus.BAD_REQUEST)
        @ResponseBody
        public String handleClientErrors(Exception ex) {
            LOGGER.error(ex.getMessage(), ex);
            return ex.getMessage();
        }
    
        @ExceptionHandler(Exception.class)
        @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
        @ResponseBody
        public String handleServerErrors(Exception ex) {
            LOGGER.error(ex.getMessage(), ex);
            return ex.getMessage();
        }
    }
    
  7. Configure Spring. As you already observed, we used annotations all over, so Spring configuration is minimal (applicationContext.xml).
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans" 
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns:context="http://www.springframework.org/schema/context" 
            xmlns:mvc="http://www.springframework.org/schema/mvc"
            xsi:schemaLocation="http://www.springframework.org/schema/beans 
                  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                  http://www.springframework.org/schema/context 
                  http://www.springframework.org/schema/context/spring-context-3.0.xsd
                  http://www.springframework.org/schema/mvc 
                  http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
    
        <context:component-scan base-package="org.zmeu.blog.spring.rest" />
    
        <mvc:annotation-driven />
    
    </beans>
    
  8. Configure web application. Our web.xml configuration is also minimal. You can see that we only registered the Spring MVC DispatcherServlet and added the Log4jConfigListener for logging.
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="2.5" 
            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">
    
        <context-param>
            <param-name>log4jConfigLocation</param-name>
            <param-value>classpath:log4j.properties</param-value>
        </context-param>
        <listener>
            <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
        </listener>
    
        <servlet>
            <servlet-name>DispatcherServlet</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:applicationContext.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>DispatcherServlet</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    
    </web-app>
    
  9. Build the project. From command line, run mvn clean install command from the directory where pom.xml is located. Build must be successful.
  10. Deploy the resulted web application. Run mvn tomcat:run from command line from the same location.
  11. Test and consume RESTful services. Below you can see the class that is testing our RESTful services. I used TestNG testing framework (JUnit can be used as well). Tests that are shown below can be used as an example of how to consume RESTful services. All tests are disabled by default @Test(enabled = false). You have to enable them. This is done in order to skip tests execution at the build time. Tests should be run only after deploying the web application. In order to execute the tests, run mvn test command.
    package org.zmeu.blog.spring.rest.controller;
    
    import static org.testng.Assert.assertEquals;
    import static org.testng.Assert.assertTrue;
    import static org.testng.Assert.fail;
    
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    
    import org.springframework.http.HttpStatus;
    import org.springframework.web.client.HttpClientErrorException;
    import org.springframework.web.client.RestTemplate;
    import org.testng.annotations.BeforeClass;
    import org.testng.annotations.Test;
    import org.zmeu.blog.spring.rest.domain.User;
    import org.zmeu.blog.spring.rest.domain.UserList;
    
    @Test(enabled = false)
    public class UserControllerTest {
        private static final String REST_SERVICE_URL =
            "http://localhost:8080/zmeu-blog-spring-rest/users";
        private RestTemplate restTemplate;
    
        @BeforeClass
        protected void beforeClass() {
            restTemplate = new RestTemplate();
        }
    
        public void create() {
            createAndAssertUser();
        }
    
        public void read() {
            User createdUser = createAndAssertUser();
            User user = restTemplate.getForObject(
                REST_SERVICE_URL + "/{userId}", User.class, createdUser.getId());
            assertUser(user, createdUser);
        }
    
        public void update() {
            User user = createAndAssertUser();
            user.setName("Updated user name");
            restTemplate.put(REST_SERVICE_URL + "/{userId}", user, user.getId());
            User updatedUser = restTemplate.getForObject(
                REST_SERVICE_URL + "/{userId}", User.class, user.getId());
            assertUser(updatedUser, user);
        }
    
        public void updateIncorrectUrl() {
            User user = createAndAssertUser();
            user.setName("Updated user name");
            try {
                restTemplate.put(
                    REST_SERVICE_URL + "/{userId}", user, user.getId() + 1);
                fail("Expecting HttpClientErrorException: 400 Bad Request");
            } catch (HttpClientErrorException e) {
                assertEquals(e.getStatusCode(), HttpStatus.BAD_REQUEST);
            }
        }
    
        public void delete() {
            User createdUser = createAndAssertUser();
            restTemplate.delete(REST_SERVICE_URL + "/{userId}", createdUser.getId());
            try {
                restTemplate.getForObject(
                    REST_SERVICE_URL + "/{userId}", User.class, createdUser.getId());
                fail("Expecting HttpClientErrorException: 400 Bad Request");
            } catch (HttpClientErrorException e) {
                assertEquals(e.getStatusCode(), HttpStatus.BAD_REQUEST);
            }
        }
    
        public void list() {
            UserList initialUsers = restTemplate.getForObject(
                REST_SERVICE_URL, UserList.class);
            User createdUser = createAndAssertUser();
            UserList users = restTemplate.getForObject(
                REST_SERVICE_URL, UserList.class);
            List<User> createdUsers = new ArrayList<User>(users.getUsers());
            createdUsers.removeAll(initialUsers.getUsers());
            assertEquals(createdUsers.size(), 1);
            assertUser(createdUsers.get(0), createdUser);
        }
    
        private User createAndAssertUser() {
            User user = new User();
            user.setId(0);
            user.setName("User name");
            user.setRegistrationDate(new Date());
            return createAndAssertUser(user);
        }
    
        private User createAndAssertUser(User user) {
            User createdUser = restTemplate.postForObject(
                REST_SERVICE_URL, user, User.class);
            assertUserNoId(createdUser, user);
            return createdUser;
        }
    
        private void assertUserNoId(User actual, User expected) {
            assertTrue(actual.getId() > 0);
            assertEquals(actual.getName(), expected.getName());
            assertEquals(actual.getRegistrationDate(),
                expected.getRegistrationDate());
        }
    
        private void assertUser(User actual, User expected) {
            assertTrue(actual.getId() > 0);
            assertEquals(actual.getName(), expected.getName());
            assertEquals(actual.getRegistrationDate(),
                expected.getRegistrationDate());
        }
    }
    
  12. Testing our RESTful services using web browser:
    RESTful service. Retrieve user by id.
    Retrieve user with id = 12
    RESTful service. Retrieve user list.
    Retrieve user list
  13. Changing the representation of your resources from XML to JSON is very easy. No changes are required to be done in controllers or client side. You have to perform the following actions only:
    • Add the following dependency to pom.xml:
      <dependency>
          <groupId>org.codehaus.jackson</groupId>
          <artifactId>jackson-mapper-asl</artifactId>
          <version>1.8.5</version>
          <scope>runtime</scope>
      </dependency>
      
    • Remove all JAXB annotations from data-type classes (User.java and UserList.java)
    Taking an arbitrary Java object returned from a controller handler method and converting it into a client-pleasing representation is a job for one of Spring’s HTTP message converters. Spring comes with a variety of message converters: XML, JSON, Atom, RSS, binary, String, etc (see all classes implementing HttpMessageConverter).
  14. You are done!

IMPORTANT: Although Spring 3 supports validation of arguments and return types for Spring MVC controllers using JSR-303 (Bean validation), it doesn’t work in case of RESTful controllers (see also bugs SPR-6709 and SPR-6928).

Also you can have a look at the following related articles:

12 comments:

  1. Hi would you be able to provide an example how to use RestTemplate to POST an ArrayList , in this case how do you POST your UserList object using RestTemplate POST method?

    ReplyDelete
    Replies
    1. In order to be able to POST an UserList you have to first add support for it in UserController. It should look something like this:

      @RequestMapping(value = "/bulk", method = RequestMethod.POST)
      @ResponseBody
      public UserList bulkCreate(@RequestBody UserList users) {
      LOGGER.info("Creating new users {}", users);
          // logic goes here, return the created users instead of input
      return users;
      }

      And then you will be able to create bulk users as follows:

      UserList createdUsers = restTemplate.postForObject(REST_SERVICE_URL + "/bulk", userList, UserList.class);

      Delete
  2. Below is a response to Uma Ravi question: "Both Get and Put have same urls. How does the request get differentiated and mapped. Please explain on this."

    HTTP defines methods (sometimes referred to as verbs) to indicate the desired action to be performed on the identified resource.

    In our case the resource (User with id=12), which is identified by a URL (http://host:port/servletContextPath/users/12) supports 3 types of actions identified by corresponding HTTP methods (GET, PUT, DELETE). Although URL is the same, the actions are different.

    It works same way as HttpServlet. You map the servlet to a URL path inside web.xml and then implement the actions which you want to support by using doGet(), doPost(), doPut(), doDelete(), etc. methods.

    ReplyDelete
  3. Hi,
    Thanks for this tutorial, but I did not see where data is coming from, for example you said in your browser tests :

    Retrieve user with id = 12

    Then

    Retrieve user list

    There is no database or hardcoded data in your code.
    thanks



    ReplyDelete
    Replies
    1. I made the screenshot after running UserControllerTest few times. UserServiceImpl uses a simple map for persistence, so all the created users stays there, in memory.

      Delete
  4. Thanks Andrei .............. Your explanation is so neat and clean .......... A beginner can understand very easily ........... Hats off to you ... Ur explanation made me to write some comments on you........... Thanks again... Keep up the good work ........... :)

    ReplyDelete
  5. Hi Andrei,

    When I am running the Unit Test , It's showing the following error .
    Note : I have changed the servletContextPath as I tried with the existing one showing the same error .


    -------------------------------------------------------
    T E S T S
    -------------------------------------------------------
    Running TestSuite
    2014-02-08 19:44:00,715 WARN [main] org.springframework.web.client.RestTemplate - POST request for "http://localhost:8080/tapanesh/users" resulted in 404 (Not found); invoking error handler
    2014-02-08 19:44:00,740 WARN [main] org.springframework.web.client.RestTemplate - POST request for "http://localhost:8080/tapanesh/users" resulted in 404 (Not found); invoking error handler
    2014-02-08 19:44:00,798 WARN [main] org.springframework.web.client.RestTemplate - GET request for "http://localhost:8080/tapanesh/users" resulted in 404 (Not found); invoking error handler
    2014-02-08 19:44:00,812 WARN [main] org.springframework.web.client.RestTemplate - POST request for "http://localhost:8080/tapanesh/users" resulted in 404 (Not found); invoking error handler
    2014-02-08 19:44:00,825 WARN [main] org.springframework.web.client.RestTemplate - POST request for "http://localhost:8080/tapanesh/users" resulted in 404 (Not found); invoking error handler
    2014-02-08 19:44:00,836 WARN [main] org.springframework.web.client.RestTemplate - POST request for "http://localhost:8080/tapanesh/users" resulted in 404 (Not found); invoking error handler
    Tests run: 6, Failures: 6, Errors: 0, Skipped: 0, Time elapsed: 1.623 sec <<< FAILURE!



    Could u plz tell me why this 404 exception happening ... ?

    I will appreciate your time .
    Thanks in Advance .

    ReplyDelete
    Replies
    1. Hi Tapanesh,

      Check your console after running mvn tomcat:run for the following message:

      [INFO] Running war on http://localhost:8080/zmeu-blog-spring-rest

      It will show you what is the actual servletContextPath. By default the maven tomcat plugin uses the artifactId for your pom.xml.

      Delete
  6. Hey, Andrei

    Thank's for your tutorial. But there is a little problem, when I try to deploy .war file on Tomcat I receive the following error: SEVERE: Error listenerStart, and when i go to Logs in Tomcat there is the following error:

    Feb 17, 2014 5:08:51 PM org.apache.catalina.core.ApplicationContext log
    INFO: Initializing log4j from [classpath:log4j.properties]
    Feb 17, 2014 5:08:51 PM org.apache.catalina.core.StandardContext listenerStart
    SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.util.Log4jConfigListener
    java.lang.IllegalArgumentException: Invalid 'log4jConfigLocation' parameter: class path resource [log4j.properties] cannot be resolved to URL because it does not exist
    at org.springframework.web.util.Log4jWebConfigurer.initLogging(Log4jWebConfigurer.java:155)
    at org.springframework.web.util.Log4jConfigListener.contextInitialized(Log4jConfigListener.java:45)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4939)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5434)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:633)
    at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:976)
    at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1653)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
    at java.util.concurrent.FutureTask.run(FutureTask.java:262)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)

    I have placed my applicationContext.xml in WEB-INF, web.xml in WEB-INF and log4j.properties in resources.

    Could you help me, please?

    Thank's!

    ReplyDelete
    Replies
    1. Hi Bogdan,

      You can download the project sources from http://azagorneanu.googlecode.com/svn/trunk/blog/zmeu-blog-spring-rest.zip

      There you will see exactly where all the files should be placed.

      Delete
  7. Hey, could you tell me how can i run the tests, or how to populate the map with users, 'cause for now i've tried all the possible paths and none of them create any new users.

    ReplyDelete
    Replies
    1. Hi,

      Run steps 9, 10 and 11. In step 11 run the UserControllerTest.read() test (you will have to enable it first, check the enabled attribute of @Test). The test can be run from your IDE, just make sure you have the TestNG plugin installed. You can use JUnit instead of TestNG, just replace the TestNG annotations with the JUnit one.

      The read() test will create and then read an user. After you will be able to read it through browser. Check step 12 and screenshots.

      Delete