Thursday, October 15, 2015

Java Spring Restful Web Service with Configurable MongoDB



Today I'll show you how to build Java RESTful Web Service that uses MongoDB database for storing and retrieving data. The service is written using Java Spring 4 technology. 

In this project I will show how to configure MongoDB database parameters in java properties file and how to consume it using Java Spring. If you're new to MongoDB then I suggest to read my previous article on How to Install and Run MongoDB on Windows.

I will not describe in details maven pom.xml structure just because you can simply download the whole project by clicking on the download link at the bottom of this page.

Here is the structure of the project:

spring-rest-webservice-mongodb-project-structure
Ok, so what this all about?

We got here Person class which represent our domain object to be persistent to MongoDB.

@Document
public class Person {

    @Id
    private String id;
    
    private String name;
    private Integer age;
    private String country;
    
    public Person(){}
    
    public Person(String name, Integer age, String country){
        setName(name);
        setAge(age);
        setCountry(country);
    }
    
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public String getCountry() {
        return country;
    }
    public void setCountry(String country) {
        this.country = country;
    }
    public String toString() {
        return "[" + getName() 
                + ", " + getAge()
                + ", " + getCountry()
                + "]";
    }
}

PersonRepository class is responsible for actual operations via MongoDB for the Person. In our case these are: addPersongetPersonByNamegetAllPersonsdeletePerson and updatePerson.

@Repository
public class PersonRepository {
    
    public static final String PERSON_COLLECTION_NAME = "person";

    @Autowired
    private MongoTemplate mongo;
    
    public void addPerson(Person person){
        if(!mongo.collectionExists(Person.class)){
            mongo.createCollection(Person.class);
        }
        mongo.insert(person, PERSON_COLLECTION_NAME);
    }
    
    public Person getPersonByName(String name) {
        return mongo.findOne(Query.query(Criteria.where("name").is(name)),
                        Person.class, PERSON_COLLECTION_NAME);
    }

    public List getAllPersons() {
        return mongo.findAll(Person.class, PERSON_COLLECTION_NAME);
    }
    
    public Person deletePerson(String name) {
        Person person = mongo.findOne(Query.query(Criteria.where("name").is(name)), 

                         Person.class, PERSON_COLLECTION_NAME);
        mongo.remove(person, PERSON_COLLECTION_NAME);
        
        return person;
    }
    
    public Person updatePerson(String name, Person person) {
        Query query = new Query();
        query.addCriteria(Criteria.where("name").is(name));
 
        Update update = new Update();
        update.set("name", person.getName());
        update.set("age", person.getAge());
        update.set("country", person.getCountry());
 
        mongo.updateFirst(query, update, Person.class);
        
        return person;
    }
}

As you can see from the code above I use here MongoTemplate and notice the @Autowired Spring annotation. Now let's see our AppConfig class:



@Configuration
@ComponentScan(basePackages = { "com.codearsenal.*" })
@PropertySource(value="classpath:config.properties", ignoreResourceNotFound=true)
public class AppConfig {

    @Autowired
    private Environment env;

    @Bean
    public MongoTemplate mongoTemplate() throws Exception {

        String db_host = env.getProperty("mongodb.host");        
        String db_port = env.getProperty("mongodb.port");
        String db_name = env.getProperty("mongodb.db");
        
        MongoClient mongo = new MongoClient(db_host, Integer.valueOf(db_port));
        MongoDbFactory mongoDbFactory = new SimpleMongoDbFactory(mongo, db_name);
        return new MongoTemplate(mongoDbFactory);

    }
}

Here in AppConfig I set up MongoTemplate configurations. @PropertySource annotation tells to Spring to load properties file config.properties. Inside config.properties I specified my MongoDB connection settings:

                mongodb.host=127.0.0.1
                mongodb.port=27017
                mongodb.db=codearsenal_db

Now let's see PersonController class. This is the entry point of all requests from client to our server.

@Controller
public class PersonController {

    private static final Logger logger = LoggerFactory
            .getLogger(PersonController.class);

    @Autowired
    private PersonRepository personRepository;

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String home(Locale locale, Model model) {
        logger.info(
                "This is Default Home REST page.\n\n The client locale is {}.",
                locale);
        Date date = new Date();
        DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG,
                DateFormat.LONG, locale);
        String formattedDate = dateFormat.format(date);
        model.addAttribute("serverTime", formattedDate);
        return "status";
    }

    @RequestMapping(value = "/persons", method = RequestMethod.GET)
    @ResponseBody
    public MultiplePersonResponse getAllPersons() {
        logger.info("getAllPersons()...");
        List persons = personRepository.getAllPersons();
        MultiplePersonResponse resp = new MultiplePersonResponse(true, persons);
        logger.info("...getAllPersons()");
        return resp;
    }

    @RequestMapping(value = "/person/{name}", method = RequestMethod.GET)
    @ResponseBody
    public SinglePersonResponse getPersonByName(
            @PathVariable("name") String name) {
        logger.info("getPersonByName()...");
        Person myPerson = personRepository.getPersonByName(name);
        if (myPerson != null) {
            logger.info("returned: " + myPerson.toString());
        } else {
            logger.info("name: " + name + ", NOT FOUND!");
        }
        SinglePersonResponse resp = new SinglePersonResponse(true, myPerson);
        logger.info("...getPersonByName()");
        return resp;
    }

    @RequestMapping(value = "/person/delete/{name}", method = RequestMethod.DELETE)
    @ResponseBody
    public BasicResponse deletePersonByName(@PathVariable("name") String name) {
        logger.info("deletePersonByName()...");
        BasicResponse resp;
        Person person = personRepository.deletePerson(name);
        if (person != null) {
            logger.info("deleted: " + person.toString());
            resp = new BasicResponse(true,
                    "Successfully deleted Person: " + person.toString());
        } else {
            logger.info("name: " + name + ", NOT FOUND!");
            resp = new BasicResponse(false"Failed to delete name: " + name);
        }
        logger.info("...deletePersonByName()");
        return resp;
    }

    @RequestMapping(value = "/person/update/{name}", method = RequestMethod.PUT)
    @ResponseBody
    public BasicResponse updatePersonByName(@PathVariable("name") String name,
            @ModelAttribute("person") Person person) {
        logger.info("updatePersonByName()...");
        BasicResponse resp;
        Person myPerson = personRepository.updatePerson(name, person);
        if (myPerson != null) {
            logger.info("updated: " + myPerson.toString());
            resp = new BasicResponse(true,
                    "Successfully updated Person: " + myPerson.toString());
        } else {
            logger.info("name: " + name + ", NOT FOUND!");
            resp = new BasicResponse(false"Failed to update Person: " + name);
        }
        logger.info("...updatePersonByName()");
        return resp;
    }

    @RequestMapping(value = "/person/addPerson", method = RequestMethod.POST)
    @ResponseBody
    public BasicResponse addPerson(@RequestBody Person person) {
        logger.info("addPerson()...");
        BasicResponse resp;
        if (person.getName() != null && person.getName().length() > 0) {
            logger.info("adding: " + person.toString());
            personRepository.addPerson(person);
            resp = new BasicResponse(true,
                    "Successfully added Person: " + person.getName());
        } else {
            logger.info("Failed to insert...");
            resp = new BasicResponse(false"Failed to insert...");
        }
        logger.info("..addPerson()");
        return resp;
    }
}

Ok, now we're ready to launch our Tomcat server to see how all this thing works, but first we should start MongoDB database server. Read my previous post if you do not know how to do this - How to Install and Run MongoDB on Windows.

Let's test our application, First we'll add a new person to our system, I will use Postman to simulate JSON requests. Just make sure that Tomcat and MongoDB servers are running.

  • addPerson:
             Request method: POST
             URL: http://localhost:8080/CodeArsenalApplication/person/addPerson
             JSON:
               {
                  "name":"Yuri",
                  "age":35,
                  "country":"USA"
               }


Postman-addPerson-rest-request


JSON response from the server:
                 {
                    "success"true,
                    "message""Successfully added Person: Yuri"
                 }

Now  we can run Robomongo to ensure that the person was really added to the database:

mongodb-person-table


  • getPersonByName:

Postman-getPersonByName-rest-request

In the same manner you can test other APIs. 
If you have any questions you're welcome to ask in the comments section.

Download spring-rest-webservice-mongodb project (Eclipse/Maven).

2 comments: