Java: Hello, World! at Platform.sh

Otavio Santana
Developer Relations
06 Jun 2019

Today, Platform.sh is announcing support for Java technologies. And in this post, we’ll cover how easy it is to either create or move your Java application to Platform.sh.

A quick refresher: a second-generation Platform-as-a-Service, Platform.sh is built especially for continuous deployment. With Platform.sh, you can host web applications in the cloud while making your development and testing workflows more productive. As the premier tool for developing and delivering applications in Drupal, WordPress, Symfony, Typo3, EZ, and Magento, Platform.sh also supports an array of languages, including PHP, Node.js, Python, Ruby, and Go.

Now, back to Java! We’ll show a Spring-Boot application connected to a MySQL database instance using Maven to automate the build and install. In the Java world, Spring Boot makes it easy to create stand-alone, production-grade, Spring-based applications that you can just run.

Show me the code

Platform.sh supports common Java tools, and so we’ll use the Spring Initializr to create a Spring Maven Project with Java 8, and append the MySQL driver as a dependency. There’s also a Platform.sh configuration library that reads Platform.sh-specific details, like routes and environmental variables, and exposes them to the application. Here’s the pom.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.mycompany</groupId>
	<artifactId>template-spring-boot-maven-mysql</artifactId>
	<version>0.0.1</version>

	<properties>
    	<java.version>1.8</java.version>
	</properties>

	<parent>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-parent</artifactId>
    	<version>2.1.5.RELEASE</version>
	</parent>

	<dependencies>
    	<dependency>
        	<groupId>org.springframework.boot</groupId>
        	<artifactId>spring-boot-starter-web</artifactId>
    	</dependency>
    	<dependency>
        	<groupId>org.springframework.boot</groupId>
        	<artifactId>spring-boot-starter-data-jpa</artifactId>
    	</dependency>
    	<dependency>
        	<groupId>mysql</groupId>
        	<artifactId>mysql-connector-java</artifactId>
    	</dependency>
    	<dependency>
        	<groupId>sh.platform</groupId>
        	<artifactId>config</artifactId>
        	<version>2.2.0</version>
    	</dependency>
	</dependencies>

	<build>
    	<finalName>spring-boot-maven-mysql</finalName>
    	<plugins>
        	<plugin>
            	<groupId>org.springframework.boot</groupId>
            	<artifactId>spring-boot-maven-plugin</artifactId>
        	</plugin>
    	</plugins>
	</build>

	<repositories>
    	<repository>
        	<id>oss.sonatype.org-snapshot</id>
        	<url>http://oss.sonatype.org/content/repositories/snapshots</url>
    	</repository>
	</repositories>
</project>

Spring Data JPA, part of the larger Spring Data family, makes it easy to quickly implement JPA-based repositories. This module deals with enhanced support for JPA-based data access layers, making it easier to build Spring-powered applications that use data access technologies. In this example, you store Person objects annotated as a JPA entity.

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.util.Objects;

@Entity
public class Person {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;

	@Column
	private String name;

	@Column
	private Integer age;


 //getter and setter
}

Spring Data JPA focuses on using JPA to store data in a relational database. Its most compelling feature is the ability to create repository implementations automatically, at runtime, from a repository interface.

import org.springframework.data.repository.PagingAndSortingRepository;

public interface PersonRepository extends PagingAndSortingRepository<Person, Long> {
}

After creating the model and the repository, the last step in the Java application is the controller with a REST. The @RequestMapping annotation provides “routing” information. It tells Spring that any HTTP request with the / path should be mapped to the home method. The @RestController annotation tells Spring to render the resulting string directly back to the caller.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("people")
public class PersonController {

	@Autowired
	private PersonRepository repository;


	@PostMapping
	@ResponseStatus(code = HttpStatus.CREATED)
	public String save(@RequestBody Person person) {
    	repository.save(person);
    	return "Saved- " + person.getName();
	}

	@GetMapping(value = "/{id}", produces = "application/json")
	public Person get(@PathVariable("id") long id) {
    	return repository.findById(id).orElseThrow(() -> new RuntimeException("Not found"));
	}

	@GetMapping(produces = "application/json")
	public Iterable<Person> get() {
    	return repository.findAll();
	}


	@PutMapping(value = "/{id}", produces = "application/json")
	public Person update(@PathVariable("id") long id, @RequestBody Person person) {
    	repository.save(person);
    	return person;
	}

	@DeleteMapping(value = "/{id}", produces = "application/json")
	public Person delete(@PathVariable("id") long id) {
    	Person person = repository.findById(id).orElseThrow(() -> new RuntimeException("Not found"));
    	repository.deleteById(id);
    	return person;
	}
}
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

}

The final part of our application is a startup. It’s a standard method that follows the Java convention for an application entry point. SpringApplication bootstraps the app, starting Spring on a Tomcat web server.

Platform.sh structure

The Java application is ready to go! The next step is to set the Platform.sh files required to manage and deploy the demo. Every application you deploy on Platform.sh is built as a virtual cluster, containing a set of containers. There are three types of containers within your cluster:

  • One Router (.platform/routes.yaml). Platform.sh allows you to define the routes.
  • Zero or more service containers (.platform/services.yaml). Platform.sh allows you to completely define and configure the topology and services you want to use on your project.
  • One or more application containers (.platform.app.yaml). You control your application and the way it will be built and deployed on Platform.sh via a single configuration file.

The services file allows you to define the services such as database, search engine, cache and so on; in this project demo, it will be a MySQL instance.

database:
  type: mysql:10.2
  disk: 1024

The routes file defines the environment’s routes; there are several options, such as cache and redirects, but we won’t cover these in this post. For more information, see the documentation.

https://{default}/:
  type: upstream
  upstream: "app:http"

The final configuration file, .platform.app.yaml, specifies a single application, and defines how to build and execute it. Here’s the structure:

  • name. Sets the unique name of the application container.
  • type. Sets the container base image to use, including application language. We’re using Java version 8, but there are more Java versions that Platform.sh supports.
  • hooks. Controls how the application gets compiled. This example uses a Maven command to compile, but there’s support for Gradle and Ant.
  • relationships. Defines connections to other services, such as the MySQL database instance.
  • web. Controls how the web application is served. As an uber-jar, the application will execute a Spring Jar file. Also, the application must use the port that’s provided from the container through a $PORT variable.
name: app
type: "java:8"

disk: 1024

hooks:
  build: mvn clean install

relationships:
  database: "database:mysql"
web:
  commands:
    start:  java -jar target/spring-boot-maven-mysql.jar --server.port=$PORT

Platform.sh and Java

For the database, we need to define where the connection comes from. Platform.sh provides connection credentials to services using the PLATFORM_RELATIONSHIPS environmental variable.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import sh.platform.config.Config;
import sh.platform.config.MySQL;

import javax.sql.DataSource;

@Configuration
public class DataSourceConfig {

	@Bean(name = "dataSource")
	public DataSource getDataSource() {
    	Config config = new Config();
    	MySQL database = config.getCredential("database", MySQL::new);
    	return database.get();
	}
}

The Java Config offers classes that will read the information from the variable and return an object that represents the services, like a DataSource for MySQL, PostgreSQL, MongoClient, Redis, or any other services you’ve defined in .platform/services.yaml!

The application is now ready, so it’s time to move it to the cloud with Platform.sh using the following steps:

  • Create a new free trial account.
  • Sign up with a new user and password, or login using a current GitHub, Bitbucket, or Google account. If you use a third-party login, you’ll be able to set a password for your Platform.sh account later.
  • Select the region of the world where your site should live.
  • Select the blank template.

After this wizard, Platform.sh will provision the whole infrastructure to you, and Platform.sh will offer a Git remote repository. Before access, remember to set the SSH keys. The Platform.sh Git-driven infrastructure means it will automatically manage everything your application needs to push it to the master remote repository. You only need to write your code—including a few YAML files that specify your desired infrastructure—then commit it to Git and push.

git remote add platform <platform.sh@gitrepository>
git commit -m "Initial project"
git push -u platform master

Code pushed will create both the Java application, a MySQL instance, and, when it’s done, will return an IP address to the service. Let’s test the application. To test a REST application, any HTTP client is OK.

curl -X POST -H "Content-Type: application/json"  -d '{"name": "Paulo", "age": 50}' https://<service.ip>/people
curl -X POST -H "Content-Type: application/json"  -d '{"name": "Otavio", "age": 30}' https://<service.ip>/people
curl -X POST -H "Content-Type: application/json"  -d '{"name": "Poliana", "age": 25}' https://<service.ip>/people
curl https://<service.ip>/people
#result output here

You’ve now seen how a native integration between Java and Platform.sh works, and the result code confirms it! As a Java developer, you can bring your application to the cloud quickly with Platform.sh once that application uses either Maven, Gradle, and Ant as build management.

There are templates for Java and other languages in our SaaS. We’re working to have even more facilities, templates models, and services to help grow the Java and Platform.sh community.