Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Making Spring Web Services With Scala

DZone's Guide to

Making Spring Web Services With Scala

Together, Spring and Scala can create simple, scalable, secure, robust web services that should be at home in the JVM ecosystem.

· Java Zone
Free Resource

Managing a MongoDB deployment? Take a load off and live migrate to MongoDB Atlas, the official automated service, with little to no downtime.

Spring is perhaps the most popular web development framework for the Java platform. Scala is a statically typed, functional programming language that runs on the JVM. Scala is highly interoperable with the Java language, so any Java lib can be used alongside Scala. Scala can be used along with Spring's ecosystem to build highly scalable, robust web applications.

The following shows how to create a Spring web application in Scala, Maven, Spring Boot
The code for this is available on GitHub.

Create a Maven Spring Boot Application

Create a Maven project and add the following content to your Maven POM file. The easy way to bootstrap a Spring Boot Maven project is by using Spring Initializer.

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.springScala</groupId>
    <artifactId>spring-scala</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>spring-scala</name>
    <description>Developing Scala based Spring applications</description>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.2.RELEASE</version>
        <relativePath />
        <!-- lookup parent from repository -->
    </parent>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.4.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-library</artifactId>
            <version>2.10.4</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>net.alchim31.maven</groupId>
                <artifactId>scala-maven-plugin</artifactId>
                <version>3.1.3</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>


Our Scala plugin used here will compile Scala code found under the src/main/scala and src/test/scala folders to Java class files (bytecode), during the compile and testCompile phases. We're also adding the Scala lib as a dependency.

Create a Scala Class Application.scala Inside src/main/scala

This is the Spring boot entrypoint class. At runtime, this class boots the Spring application and starts the spring context. The Object keyword is used to create a singleton instance of the class in Scala.

@SpringBootApplication
class Application

object Application extends App {
    SpringApplication.run(classOf[Application]);
}


Make It RESTful

@RestController and @RequestMapping are used to create RESTful web services in Spring. A Controller class sample in Scala can be seen below:

@RestController
@RequestMapping(path = Array("/api"))
class UserController(@Autowired val userService: UserService, @Autowired val dataSource: DataSource) {
    @GetMapping(path = Array("/users"))
    def getAllUsers(): Iterable[Users] = {
        userService.listUsers
    }

    @GetMapping(path = Array("/users/{id}"))
    def getUser(@PathVariable id: Long): Users = {
        userService.getUser(id)
    }

    @PostMapping(path = Array("/users"))
    def createUser(@RequestBody users: Users): ResponseEntity[Long] = {
        val id = userService.createUser(users)
        new ResponseEntity(id, new HttpHeaders, HttpStatus.CREATED)
    }
}


Making a Service Bean

Here's a sample Service bean in Scala. The service bean is where the web service logic goes into:

@Service
class UserService(@Autowired private val userRepository: UserRepository) {
    def listUsers(): Iterable[Users] = {
        userRepository.findAll
    }

    def getUser(id: Long): Users = {
        userRepository.findOne(id)
    }

    def createUser(users: Users): Long = {
        userRepository.save(users)
        users.id
    }
}


Adding Spring Data JPA

The Spring Data JPA Repository class looks like below: 

@Repository
trait UserRepository extends CrudRepository[Users, Long] {
  def findUserByUsername(username: String): Users
}


Adding the Entity Class

Now let's tackle the JPA Entity class. This can also be a case class, as popularly used in scala. But for simplicity, I am keeping it as just a scale class.

@Entity
class Users extends Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @BeanProperty
    var id: Long = _

    @BeanProperty
    @Column(name = "username")
    var username: String = _

    @BeanProperty
    @Column(name = "password")
    var password: String = _

    @BeanProperty
    @Column(name = "enabled")
    var enabled: Boolean = _

}


Adding the Data Source

An embedded H2 Database is used here for demonstration purposes. Spring Boot will create the H2 data source if H2 JARs are available on the classpath. The configuration required for the H2 database is in application.properties inside src/main/resources. 

spring.datasource.url=jdbc:h2:~/test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=sa
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect


The H2 web console, a client for the H2 embedded database, can also be created using the following configuration. The default username/password would be sa/sa. This would create an H2 console at http://localhost:8080/h2-console:

@Configuration
class H2Config {
    @Bean def h2servletRegistration(): ServletRegistrationBean = {
        val registrationBean = new ServletRegistrationBean(new WebServlet)
        registrationBean.addUrlMappings("/console/*")
        return registrationBean
    }
}


Spring Boot also runs SQL with the name import.sql inside the resources folder during the startup.

Optional Swagger Configuration

Swagger is a REST web services documentation tool. Swagger also has a nice UI to test your services on the fly. Create a Swagger Configuration class. This would create a swagger UI at http://localhost:8080/swagger-ui.html:

@Configuration
@EnableSwagger2
class SwaggerConfig {
    @Bean
    def api(): Docket = {
        new Docket(DocumentationType.SWAGGER_2).select.apis(RequestHandlerSelectors.any).paths(PathSelectors.any).build
    }
}


Swagger exposes the various the REST APIs in the controller. This can be used to test the services.

Securing REST APIs With Spring Security

Create a Scala class called WebSecurityConfig:

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
class WebSecurityConfig(@Autowired val dataSource: DataSource) extends WebSecurityConfigurerAdapter {
    override def configure(http: HttpSecurity) = {
        http.authorizeRequests.antMatchers("/console", "/console/**", "/console/", "/swagger-ui.html", "/**/*.css", "/**/*.js", "/**/*.png", "/configuration/**", "/swagger-resources", "/v2/**").permitAll
        http.authorizeRequests.anyRequest.authenticated
        http.csrf.disable
        http.headers.frameOptions.disable
        http.httpBasic
    }

    @Bean override def userDetailsService: UserDetailsService = {
        val manager = new JdbcDaoImpl
        manager.setDataSource(dataSource)
        manager
    }
}


This Spring security configuration enables basic HTTP authentication. HTTP authentication is required to access the APIs after this change. The users inserted into the database using import.sql root/root can be used to authenticate.

@EnableGlobalMethodSecurity(prePostEnabled = true) enables Spring Security's method-level security. A Service bean can be added with method-level security:

@Service
class UserService(@Autowired private val userRepository: UserRepository) {
  @PreAuthorize("hasRole('admin')")
  def listUsers(): Iterable[Users] = {
    userRepository.findAll
  }

  @PreAuthorize("hasRole('user')")
  @PostAuthorize("returnObject.username==principal.username || hasRole('admin')")
  def getUser(id: Long): Users = {
    userRepository.findOne(id)
  }

  @PreAuthorize("hasRole('admin')")
  def createUser(users: Users): Long = {
    userRepository.save(users)
    users.id
  }
}


@PreAuthorize and @PostAuthorize can be used to add method-level validations to the application. This basically only allows listUsers() and createUser() operations if the logged-in user has an admin role. Meanwhile, this basically only allows getUser() operations if the logged-in user has a role of 'user'. These roles come from the AUTHORITIES table for different users. The following lines in import.sql will create these roles:

CREATE TABLE authorities (id bigint auto_increment not null, username varchar_ignorecase(50) not null, authority varchar_ignorecase(50) not null, constraint fk_authorities_users foreign key(username) references users(username));
INSERT INTO users (id, username, password,enabled) VALUES (1, 'root', 'root', true), (2, 'user', 'user', true);
INSERT INTO authorities (id, username, authority) VALUES (1, 'root', 'ROLE_user'), (2, 'root', 'ROLE_admin'), (3, 'user', 'ROLE_user');


Testing Web Service Endpoints in Spring Test and Scala

Here's our Spring Integration test class using Spring Test. @SpringBootTest creates a web application context and injects a RestTemplate for testing APIs.

@RunWith(classOf[SpringRunner])
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class UserTests {
    @Autowired
    var template: TestRestTemplate = _

    @Test def testPostCreateUser() = {
        val headers = new HttpHeaders
        headers.add("Authorization", "Basic " + new String(Base64.encodeBase64(("root" + ":" + "root").getBytes)))
        headers.setContentType(MediaType.APPLICATION_JSON)
        headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON))
        val user = new Users
        user.setId(101)
        user.setUsername("Test")
        user.setPassword("Test")
        user.setEnabled(true)
        val entity = new HttpEntity(user, headers)
        val result = template.postForObject("/api/users", entity, classOf[String])
        println(result)
    }
}


And there you have it! Scala is the most popular functional programming language on the JVM, and Spring is the most popular framework for Java app development. Scala, with Spring, can be used to easily build scalable web services that run on the JVM.

MongoDB Atlas is the easiest way to run the fastest-growing database for modern applications — no installation, setup, or configuration required. Easily live migrate an existing workload or start with 512MB of storage for free.

Topics:
spring ,scala ,web services ,java ,tutorial

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}