Create a Springboot project and deploy it to cloudControl

Create a Springboot project and deploy it to cloudControl

In this blog entry we will learn how to create a simple Springboot project and deploy it to cloudcontrol.
It provides a REST server with SpringData and MongoDB as storage.

The web application will let us create a person with a first and last name, find a person by it’s last name as well as find all persons. The data is served as JSON or HTML.

To make our app cloud ready we will adjust the settings and deploy it to cloudControl.

I don’t want to read, gimme sources!

Create the build file

We will Gradle as our dependency and build management system. Create a build.gradle file with the following contents

buildscript {
ext {
springBootVersion = '1.1.5.RELEASE'
}
repositories {
maven { url "http://repo.spring.io/libs-snapshot" }
mavenLocal()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}


apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'spring-boot'
apply plugin: 'application'

mainClassName = "de.siobra.exampleapp.Application"
applicationName = "springboot-gradle-cctrl-example"
sourceCompatibility = 1.7

repositories {
mavenCentral()
maven { url "http://repo.spring.io/libs-snapshot" }
}

dependencies {
compile("org.springframework.boot:spring-boot-starter-web:${springBootVersion}")
compile("org.springframework.boot:spring-boot-starter-data-mongodb")
compile("org.springframework.boot:spring-boot-starter-thymeleaf:${springBootVersion}")
compile 'org.springframework:spring-context:4.0.6.RELEASE'
}

sourceSets {
main {
resources {
srcDirs 'src/main/resources'
}
}
}
task wrapper(type: Wrapper) {
gradleVersion = '2.0'
}

task stage(dependsOn: 
['clean', 'installApp'])

Note that you might have to adjust the versions. Also you can add „apply plugin: ‚idea'“ in case you are using Intellij.

Create the basic classes

First create Person.class with getters and setters (those are needed for form binding later on).

import org.springframework.data.annotation.Id;

public class Person {

@Id
private String id;

private String firstName;
private String  lastName;

public Person() {
}

public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}

public String getFirstName() {
return firstName;
}

public String getLastName() {
return lastName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public String getId() {
return id;
}
}

Next let’s create our CRUD repository (and by „we“ I mean let SpringData do the hard work for us).

import java.util.List;

import org.springframework.data.mongodb.repository.MongoRepository;

import de.siobra.exampleapp.models.Person;

public interface PersonRepository extends MongoRepository<Person, String> {

public List<Person> findByLastName(String lastName);
public List<Person> findByFirstName(String firstName);
}

Note that SpingData uses a name convention, so we don’t even need to specify our query as it will be derived from the method name. So SpringData will essentially generate e.g. the following query for as at runtime (replacing ‚${lastName}‘).

db.person.find({lastName:'${lastName}'})

Last but not least let’s create our simple controller which let’s us creating a person, retrieving all persons and searching for persons with specific last name. The ‚@Controller‘ annotation will tell Spring that this class is a controller. ‚@RequestMapping‘ specifies the mapping for an action as well as the MediaType it returns and which requests it will answer. ‚@ResponseBody‘ let’s the action return a list of persons in JSON format(or whatever format you specify) and not a view. While the actions without ‚@ResponseBody‘ and a String as return value will return a view with the name of the returned String.

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import de.siobra.exampleapp.models.Person;
import de.siobra.exampleapp.repositories.PersonRepository;

@Controller
public class PersonController {
@Autowired
PersonRepository personRepository;

@RequestMapping(value = "/persons/{lastName}", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
@ResponseBody
List<Person> showPersons(@PathVariable String lastName) {
if (lastName != null && !lastName.isEmpty()) {
return personRepository.findByLastName(lastName);
}
return personRepository.findAll();
}

@RequestMapping(value = "/persons", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
@ResponseBody
List<Person> showPersons() {
return personRepository.findAll();
}

@RequestMapping(value = "/persons", produces = MediaType.TEXT_HTML_VALUE, method = RequestMethod.GET)
String showPersonsView() {
return "persons";
}

@RequestMapping(value = "/persons", method = RequestMethod.POST)
@ResponseBody
Person newPerson(@ModelAttribute Person person) {
person = personRepository.save(person);

return person;
}
}

Create the important configuration classes

Main class

First our main class. As cloud hosters expose their configuration for applications via environment variables, we need to see if they are present and if so, use them.
First we need to know our port on which the server will be able to run. Port 8080 is usually not available on cloud platforms.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.context.annotation.ComponentScan;

import de.siobra.exampleapp.repositories.PersonRepository;

@ComponentScan
@EnableAutoConfiguration
public class Application implements EmbeddedServletContainerCustomizer{

@Autowired PersonRepository personRepository;
@Value("${PORT:}")
String port;

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}

public void customize(ConfigurableEmbeddedServletContainer container) {
if (port == null || port.isEmpty()) {
port = "8080";
}
int portInt = Integer.valueOf(port);
container.setPort(portInt);
}
}

The important part here is

@Value("${PORT:}")
String port;

This will essentially read the environment variable „PORT“. If not using cloudControl check what the name of the port variable is called at your prefered cloud hoster. Next we need to tell Spring which port to use, it’s as easy as:

container.setPort(portInt);

It is important though to implement „EmbeddedServletContainerCustomizer“ and actually override the „customize()“ Method!
For the meaning of the other annotations please consult the Spring Boot references.

MongoDB config class

Next we need a configuration class, knowing whether to use a local mongoDB instance or a remote one. Again at cloudControl the MongoSoup configuration is exposed as environment variable which looks like this

MONGOSOUP_URL: mongodb://${USER}:${PASSWORD}@${DATABASE}/cc_${DBNAME}

All we need to do know is parse that String and extract the relevant details, after checking if it’s present.

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;

import com.mongodb.Mongo;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;

@Configuration
@EnableMongoRepositories
public class MongoConfig extends AbstractMongoConfiguration {
private String host = "localhost";
private int port = 27017;
private String databaseName = "exampleDB";

@Value("${MONGOSOUP_URL:}")
private String mongoURL;
private String ccDBName;

@Override
protected String getDatabaseName() {
if (ccDBName != null) {
return ccDBName;
}
return databaseName;
}

@Override
public Mongo mongo() throws Exception {
if (mongoURL != null && !mongoURL.isEmpty()) {
Matcher m = Pattern.compile("(cc_.*)").matcher(mongoURL);
if (m.find())
ccDBName = m.group(1);

return new MongoClient(new MongoClientURI(mongoURL));
}
return new MongoClient(host, port);
}
}

Again we are reading an environment variable („MONGOSOUP_URL“). After that we’ll parse out the DB name and create a new MongoClient instance. If the environment variable is not present, we’ll just use localhost and the default mongoDB port (27017).

Round-up

Now you learn all you need to know for deploying a Spring Boot app to a cloud hoster. If unsure about the environment variables, please consult your hoster’s documentation. Especially the mongoDB configuration can vary, but you should be able to parse the relevant details from any kind of String and set them like shown above.

Sources

All sources are available on my Github account Agraphie’s Github.

There you also see how to deploy the app to cloudcontrol

2017-05-05T15:16:36+00:0018. August 2014|News|

Teile diesen Beitrag. Wähle deine Plattform!