Cloud Computing with Amazon Web Services

Wojciech Józefek

Nowadays cloud computing is really common depending on the services that we would like to use, from frequently utilized software applications to development environments, virtual machines and storage.

What is Cloud Computing?

Cloud computing is a paradigm that enables access to system resources and services that can be provisioned quickly for users, frequently over the Internet.

Nowadays cloud computing is really common depending on the services that we would like to use, from frequently utilized software applications to development environments, virtual machines and storage.

Service Models

Cloud computing is divided into service models that are represented as layers in a stack. Each layer provides increasing abstraction:

Infrastructure as a Service (IaaS)

This layer provides high-level APIs to deal with low-level details of the network infrastructure giving you access to the computing infrastructure, physical or virtual machines, object/file-based storage, firewalls, load balancers etc.

Platform as a Service (PaaS)

The layer provides you with computing platforms such as development environment for application developers, typically including an operating system, programming language execution environment, databases and web servers as well.

Software as a Service (SaaS)

The layer ensures users can access application software from cloud clients. The software is installed and deployed by a cloud provider, so there is no need to install it on a cloud user’s computer.

Examples of Cloud Providers
  • IaaS: Amazon Web Services (AWS), Google Compute Engine, Microsoft Azure
  • PaaS: AWS Elastic Beanstalk, Heroku, Google App Engine
  • SaaS: Email software such as Gmail; Social media: Facebook, Twitter

Spring Cloud Integration with Amazon Web Services

Let’s demonstrate how cloud computing works by implementing a sample application that will be integrated with Amazon S3 (Simple Storage Service) that is an IaaS object-based storage service and one of multiple services provided by Amazon Web Services.

The full list of the Amazon Web Services products is available here.

To integrate it, we will use the Spring Cloud AWS module that is one of the projects from the Spring Cloud stack providing integration with Amazon Web Services in an easy and comfortable way.

Technology Stack:

  • Maven
  • Spring Boot
  • Spring Cloud and Spring Cloud AWS
  • React: on the client-side to provide support for the UI and file upload

First of all, let’s generate the AWS security credentials: access key and secret key that are used for authentication and authorization in order to interact with the AWS.

We can do it by:

  1. Sign in to the AWS console: https://aws.amazon.com/console/
  2. Click on your account name -> My Security Credentials.
  3. Visit the Access Key Section and click the Create New Access Key button.

Once we’re done with the credentials, we can create a new S3 bucket where our objects (files) will be stored. To do this, simply:

  1. Click the S3 option under the Storage section from the main AWS console webpage (you can see it in the picture below)
  2. Click the Create bucket button.

The bucket name will be further used in our application configuration.

The Main AWS Console Window
The Main AWS Console Window

Implementation of the Sample Application

The pom.xml file defining the necessary Maven dependencies is available in the repository mentioned at the end of the article, so you can just copy the necessary dependencies from there.

Once we have all the dependencies, we can create a class that will run our application:

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

Now, we can provide the necessary configuration to integrate AWS S3 with our application in application.properties:

Let’s clarify what particular properties mean:

  • accessKey and secretKey indicate the AWS credentials that we have generated.
  • s3.bucket is the name of your created bucket.
  • region.static represents the location of one of the AWS data centers that provide services for cloud users.

Next, let’s create a configuration class to instantiate and configure the necessary Spring beans:

@Configuration
public class AWSConfiguration {

    @Value("${cloud.aws.credentials.accessKey}")
    private String accessKey;

    @Value("${cloud.aws.credentials.secretKey}")
    private String secretKey;

    @Value("${cloud.aws.region.static}")
    private String region;

    @Bean
    public BasicAWSCredentials basicAWSCredentials() {
        return new BasicAWSCredentials(accessKey, secretKey);
    }

    @Bean
    public AmazonS3Client amazonS3Client(AWSCredentials awsCredentials) {
        AmazonS3Client amazonS3Client = new AmazonS3Client(awsCredentials);
        amazonS3Client.setRegion(Region.getRegion(Regions.fromName(region)));
        return amazonS3Client;
    }
}

The fields annotated with the @Value annotation represent the properties defined in application.properties. The basicAWSCredentials bean represents the AWS credentials and the AmazonS3Client bean provides the client for accessing the Amazon S3 web service.

Let’s implement a service that will interact with the injected AmazonS3Client bean to integrate with AWS S3:

public interface StorageService {
    List<PutObjectResult> upload(MultipartFile[] multipartFiles);
}

@Service
public class AWSStorageService implements StorageService {

    private final AmazonS3Client amazonS3Client;

    @Value("${cloud.aws.s3.bucket}")
    private String bucket;

    @Autowired
    public AWSStorageService(AmazonS3Client amazonS3Client) {
        this.amazonS3Client = amazonS3Client;
    }

    public List<PutObjectResult> upload(MultipartFile[] multipartFiles) {
        List<PutObjectResult> putObjectResults = new ArrayList<>();

        Arrays.stream(multipartFiles)
                .filter(multipartFile -> !StringUtils.isEmpty(multipartFile.getOriginalFilename()))
                .forEach(multipartFile -> {
                    try {
                        putObjectResults.add(upload(multipartFile.getInputStream(), multipartFile.getOriginalFilename()));
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                });

        return putObjectResults;
    }

    private PutObjectResult upload(InputStream inputStream, String uploadKey) {
        PutObjectRequest putObjectRequest = new PutObjectRequest(bucket, uploadKey, inputStream, new ObjectMetadata());
        putObjectRequest.setCannedAcl(CannedAccessControlList.PublicRead);
        PutObjectResult putObjectResult = amazonS3Client.putObject(putObjectRequest);
        IOUtils.closeQuietly(inputStream);
        return putObjectResult;
   }
}

Here we’re injecting the AmazonS3Client bean to interact with the S3 web service.

We also need to specify a bucket to which we’ll upload objects. We’re certainly use the bucket specified in the application.properties file that is injected with the @Value annotation.

Once our service is ready, we can write a controller that will accept HTTP requests carrying files to upload, and inject the created service to upload our files.

@RestController
public class UploadController {

    private final StorageService storageService;

    @Autowired
    public UploadController(StorageService storageService) {
        this.storageService = storageService;
    }

    @RequestMapping(value = "/", method = RequestMethod.POST)
    public List<PutObjectResult> upload(@RequestParam("file") MultipartFile[] multipartFiles) {
        return storageService.upload(multipartFiles);
    }
}

Now that our file upload components are ready, it is time to deal with the client-side in order to allow file upload via the web browser. To make Spring Boot work with React, let’s create a controller serving a React single-page application (SPA):

@Controller
public class HomeController {

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String index() {
        return "index.html";
    }
}

The controller returns the index.html document where our React application resides so that it would be able to render when we visit the application root URL: http://localhost:8080/.

Finally, let’s implement the client-side code that will be sent with an HTTP response and executed in a client user’s web browser:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Spring Cloud AWS Example</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.24.0/babel.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
    <script src="https://code.jquery.com/jquery-3.2.1.min.js"
            integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
            crossorigin="anonymous"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
    class FormUpload extends React.Component {
        upload(e) {
            var formData = new FormData();
            formData.append('file', this.refs.file.files[0]);

            $.ajax({
                url: 'http://localhost:8080/',
                data: formData,
                processData: false,
                contentType: false,
                type: 'POST',
                success (data) {
                    alert('The file has been uploaded.');
                }
            });

            e.preventDefault();
        }

        render() {
            return (
                    <div>
                        <form ref="uploadForm" className="uploader" encType="multipart/form-data" >
                            <input ref="file" type="file" name="file" className="upload-file"/>
                            <input type="button" ref="button" value="Upload" onClick={this.upload.bind(this)} />
                        </form>
                    </div>
            );
        }
    }

    ReactDOM.render(<FormUpload/>, document.getElementById('root'));
</script>
</body>
</html>

Please note that the React JS code written here is not perfect, since I’m not experienced in this library, as I’m rather a newcomer. 🙂

You can compile and run the application with the following Maven command:

mvn spring-boot:run

By default, the application will run on the port 8080.

To see whether the file has been uploaded, visit your S3 bucket in the AWS console that you can refer to by: https://s3.console.aws.amazon.com/s3/buckets/your-s3-bucket-name-set-in-application.

The entire project can be found in the following repository: https://github.com/wjoz/spring-boot-cloud-aws-reactjs-example.

Summary

Spring Cloud AWS simplifies integration of your Spring applications with the AWS in a great way, so I think it is worth trying. There are lots of various AWS products that can speed up the development and deployment of your applications, therefore, I believe everyone will find services that are suitable.

The code introduced here is based on Brant Hwang’s example that you can also refer to: https://github.com/brant-hwang/spring-cloud-aws-example.

References

Poznaj mageek of j‑labs i daj się zadziwić, jak może wyglądać praca z j‑People!

Skontaktuj się z nami