Monday, 25 September 2017

Updating Cloud Foundry CLI using Brew

Need to upgrade the CF CLI using brew it's as simple as below. Go to love brew

pasapicella@pas-macbook:~$ brew upgrade cf-cli
==> Upgrading 1 outdated package, with result:
cloudfoundry/tap/cf-cli 6.31.0
==> Upgrading cloudfoundry/tap/cf-cli
Warning: Use cloudfoundry/tap/cloudfoundry-cli instead of deprecated pivotal/tap/cloudfoundry-cli
==> Downloading https://cli.run.pivotal.io/stable?release=macosx64-binary&version=6.31.0&source=homebrew
==> Downloading from https://s3-us-west-1.amazonaws.com/cf-cli-releases/releases/v6.31.0/cf-cli_6.31.0_osx.tgz
######################################################################## 100.0%
==> Caveats
Bash completion has been installed to:
  /usr/local/etc/bash_completion.d
==> Summary
🍺  /usr/local/Cellar/cf-cli/6.31.0: 6 files, 17.6MB, built in 16 seconds

pasapicella@pas-macbook:~$ cf --version
cf version 6.31.0+b35df905d.2017-09-15

Friday, 15 September 2017

Using Cloud Foundry CUPS to inject Spring Security credentials into a Spring Boot Application

The following demo shows how to inject the Spring Security username/password credentials from a User Provided service on PCF, hence using the VCAP_SERVICES env variable to inject the values required to protect the application using HTTP Basic Authentication while running in PCF. Spring Boot automatically converts this data into a flat set of properties so you can easily get to the data as shown below.

The demo application can be found as follows

https://github.com/papicella/springsecurity-cf-cups

The application.yml would access the VCAP_SERVICES CF env variable using the the Spring Boot flat set of properties as shown below.

eg:

VCAP_SERVICES

System-Provided:
{
 "VCAP_SERVICES": {
  "user-provided": [
   {
    "credentials": {
     "password": "myadminpassword",
     "username": "myadminuser"
    },
    "label": "user-provided",
    "name": "my-cfcups-service",
    "syslog_drain_url": "",
    "tags": [],
    "volume_mounts": []
   }
  ]
 }
}
...

application.yml

spring:
  application:
    name: security-cf-cups-demo
security:
  user:
    name: ${vcap.services.my-cfcups-service.credentials.username:admin}
    password: ${vcap.services.my-cfcups-service.credentials.password:password}

Thursday, 14 September 2017

Oracle 12c Service Broker for Pivotal Cloud Foundry

The following example is a PCF 2.0 Service Broker written as a Spring Boot application. This is just an example and should be evolved to match a production type setup in terms oracle requirements. This service broker simple creates USERS and assigns then 20M of quota against a known TABLESPACE

It's all documented as follows

https://github.com/papicella/oracle-service-broker




Monday, 4 September 2017

Introducing Pivotal MySQL*Web, Pivotal’s New Open Source Web-Based Administration UI for MySQL for Pivotal Cloud Foundry

Recently Pivotal announced "Pivotal MySQL*Web" on it's main blog page. You can read more about it here which was an Open Source project I created a while ago for Pivotal MySQL instances on Pivotal Cloud Foundry

https://content.pivotal.io/blog/introducing-pivotal-mysql-web-pivotal-s-new-open-source-web-based-administration-ui-for-mysql-for-pivotal-cloud-foundry


Couchbase Service Broker for Pivotal Cloud Foundry

The following example is a PCF 2.0 Service Broker written as a Spring Boot application for Couchbase 4.6.x. This is just an example and should be evolved to match a production type setup in terms of bucket creation and access for created service instances.

It's all documented as follows

https://github.com/papicella/couchbase-service-broker




More Information

https://docs.pivotal.io/tiledev/service-brokers.html

Thursday, 27 July 2017

Accessing Pivotal Cloud Foundry droplet file system when "cf ssh" isn't enabled

In order to view your application layout you can simply use "cf ssh" to log into the container and then view the files created as part of the droplet. The problem is "cf ssh" isn't always enabled bye the Ops team so what is your alternative in cloud foundry?

You can use "cf curl" to invoke an endpoint using the application GUID as shown in the steps below.

** cf ssh demo **

pasapicella@pas-macbook:~/temp/droplets$ cf ssh pas-swagger-demo
vcap@ef9e4e93-0df9-47a7-5351-dccf:~$ ls -lartF
total 16
-rw-r--r-- 1 vcap vcap  675 Apr  9  2014 .profile
-rw-r--r-- 1 vcap vcap 3637 Apr  9  2014 .bashrc
-rw-r--r-- 1 vcap vcap  220 Apr  9  2014 .bash_logout
drwxr-xr-x 2 vcap vcap    6 Jun 14 03:32 deps/
drwxr-xr-x 1 vcap root   72 Jun 14 03:32 app/
-rw-r--r-- 1 vcap vcap 1087 Jun 14 03:32 staging_info.yml
drwxr-xr-x 2 vcap vcap    6 Jun 14 03:32 logs/
drwx------ 1 vcap vcap   76 Jun 14 03:32 ./
drwxr-xr-x 1 root root   18 Jul 26 23:45 ../
drwxr-xr-x 4 vcap vcap   92 Jul 26 23:48 tmp/
vcap@ef9e4e93-0df9-47a7-5351-dccf:~$

** Steps **


1. Download droplet as follows

Format:

   cf curl /v2/apps/`cf app {appname} --guid`/droplet/download > droplet.tar.gz

Example:

pasapicella@pas-macbook:~/temp/droplets$ cf curl /v2/apps/`cf app pas-swagger-demo --guid`/droplet/download > droplet.tar.gz

To determine the app name you can either use Applications manager UI or use "cf apps" to get the app name


2. This will take some time due to the size of the droplet but when done verify you have this on the file system

pasapicella@pas-macbook:~/temp/droplets$ ls -la
total 150736
drwxr-xr-x   3 pasapicella  staff       102 Jul 27 14:20 .
drwxr-xr-x  23 pasapicella  staff       782 Jul 27 14:19 ..
-rw-r--r--   1 pasapicella  staff  77173173 Jul 27 14:23 droplet.tar.gz

3. Gunzip followed by tar -xvf and you will then have a file system replicator of what your application droplet looks like in CF

pasapicella@pas-macbook:~/temp/droplets$ d
total 313408
drwxr-xr-x   2 pasapicella  staff         68 Jun 14 13:32 deps/
drwxr-xr-x   6 pasapicella  staff        204 Jun 14 13:32 app/
drwxr-xr-x   2 pasapicella  staff         68 Jun 14 13:32 tmp/
-rw-r--r--   1 pasapicella  staff       1087 Jun 14 13:32 staging_info.yml
drwxr-xr-x   2 pasapicella  staff         68 Jun 14 13:32 logs/
drwxr-xr-x  23 pasapicella  staff        782 Jul 27 14:19 ../
-rw-r--r--   1 pasapicella  staff  160460800 Jul 27 14:23 droplet.tar
drwxr-xr-x   8 pasapicella  staff        272 Jul 27 14:25 ./


You really only want to do this to see how your application was staged on the file system as the buildpack may have changed some files or added files based on what you deployed. This is not how you would debug an application but rather view what the file system looks like for your application itself and what content exists in the files should the buildpack have changed file content for example.

Monday, 24 July 2017

Pivotal Cloud Foundry Isolation Segments Applications demo

PCF Isolation Segments Tile allows operators to isolate deployment workloads into dedicated resource pools called isolation segments. You can read more about how to install the Tile at the following location.

https://docs.pivotal.io/pivotalcf/1-11/opsguide/installing-pcf-is.html

In this demo I will show how you can configure your ORGS/spaces to use an isolation segment and then finally show that it is indeed using the isolated Diego Cells assigned to the segment tile at install time.

1. Determine the isolation segment name as per the Ops Manager title



2. Ensure you have CF CLI version as 6.26 or higher as shown below

pasapicella@pas-macbook:~$ cf --version
cf version 6.28.0+9e024bdbd.2017-06-27

3. First, you need to register an Isolation Segment with the cloud controller.

Note: you will need a prividledged user such as admin to perform this task

$ pasapicella@pas-macbook:~$ cf create-isolation-segment segment1
Creating isolation segment segment1 as admin...
OK

4. After registering the Isolation Segment, you can associate it with an Org

pasapicella@pas-macbook:~$ cf enable-org-isolation pas-org segment1
Enabling isolation segment segment1 for org pas-org as admin...
OK

5. List isolation segments against targeted ORGS as follows

pasapicella@pas-macbook:~$ cf isolation-segments
Getting isolation segments as admin...
OK

name       orgs
shared
segment1   pas-org

6. After associating an Isolation Segments with an Org, you then set it on a Space

- First target the ORG you wish to use

pasapicella@pas-macbook:~$ cf target -o pas-org
api endpoint:   https://api.yyyy.pcfdemo.yyy
api version:    2.82.0
user:           admin
org:            pas-org
No space targeted, use 'cf target -s SPACE'

- Now set the isolation segment on your desired space

pasapicella@pas-macbook:~$ cf set-space-isolation-segment isolation-segment-space segment1
Updating isolation segment of space isolation-segment-space in org pas-org as admin...
OK

In order to move running applications to this isolation segment, they must be restarted.

7. Log in as a user of the ORG/SPACE now rather then ad admin user. We will be using a
non privileged user now to push our app who has access to the ORG/Space we targeted
above. Below just shows we have switched to a different user here.

pasapicella@pas-macbook:~$ cf target
api endpoint:   https://api.yyyy.pcfdemo.yyy
api version:    2.82.0
user:           pas
org:            pas-org
space:          isolation-segment-space

8. Push an application to the space "isolation-segment-space"

pasapicella@pas-macbook:~/piv-projects/PivotalSpringBootJPA$ cf push -f manifest-inmemory-db.yml
Using manifest file manifest-inmemory-db.yml

Creating app pas-albums in org pas-org / space isolation-segment-space as pas...
OK

....

0 of 1 instances running, 1 starting
0 of 1 instances running, 1 starting
1 of 1 instances running

App started


OK

...


     state     since                    cpu      memory           disk           details
#0   running   2017-07-24 02:33:38 PM   225.3%   330.1M of 512M   162.8M of 1G

9. Identify IP address of diego CELL the app is running in

pasapicella@pas-macbook:~/piv-projects/PivotalSpringBootJPA$ cf curl /v2/apps/$(cf app pas-albums --guid)/stats | jq '.[].stats.host'
"10.10.10.71"

With this information you can verify the IP address of the Diego Cells you have for the Isolation Segment as follows using Pivotal Ops Manager Tile tabs. From the images below it's clear 10.10.10.71 is a diego cell IP address of our isolation segment.





More Information


Wednesday, 14 June 2017

Swagger UI with Spring Boot 1.5.x

I recently created this demo / blog entry on using HTTPIE with Spring Boot Rest Repositories as shown below.

http://theblasfrompas.blogspot.com.au/2017/05/using-httpie-with-spring-boot-rest.html

I decided to take that same example and add Swagger UI to the RESTful endpoints. The full source code is here.

https://github.com/papicella/httpie-springboot

In short what you need is the following maven dependancies and that will add all you need. I found it works much cleaner if you use the same version of both these dependancies for some reason

  
<dependency>
  <groupId>io.springfox</groupId>
  <artifactId>springfox-swagger2</artifactId>
  <version>2.6.1</version>
</dependency>
<dependency>
  <groupId>io.springfox</groupId>
  <artifactId>springfox-swagger-ui</artifactId>
  <version>2.6.1</version>
</dependency>

Finally a Class file describing the config and enabling Swagger is required as follows
  
package pivotal.io.boot.httpie.demo;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import static springfox.documentation.builders.PathSelectors.regex;

@Configuration
@EnableSwagger2
public class SwaggerConfig
{
    @Bean
    public Docket swaggerSpringMvcPlugin() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage("pivotal.io.boot.httpie.demo"))
                .paths(regex("/api/employee/emps.*"))
                .build()
                .apiInfo(metaData());
    }

    private ApiInfo metaData() {
        ApiInfo apiInfo = new ApiInfo(
                "Spring Boot Employee REST API",
                "Spring Boot Employee REST API",
                "1.0",
                "Terms of service",
                new Contact("Pas Apicella", "https://www.blogger.com/profile/09389663166398991762", "papicella@pivotal.io"),
                "Apache License Version 2.0",
                "https://www.apache.org/licenses/LICENSE-2.0");
        return apiInfo;
    }
}

The GitHub repo also included a Pivotal Cloud Foundry manifest.yml file to make it easy to deploy to Pivotal Cloud Foundry. The example uses a static hostname BUT can easily be changed to use a random-route or alter the hostname itself.

applications:
- name: pas-swagger-demo
  memory: 1G
  instances: 1
  hostname: pas-swagger-demo
  path: ./target/httpie-springboot-0.0.1-SNAPSHOT.jar
  env:
    JAVA_OPTS: -Djava.security.egd=file:///dev/urando

Then it's the simple "cf push"

$ cf push

pasapicella@pas-macbook:~/piv-projects/httpie-springboot$ cf push
Using manifest file /Users/pasapicella/piv-projects/httpie-springboot/manifest.yml

Creating app pas-swagger-demo in org apples-pivotal-org / space development as papicella@pivotal.io...
OK

Creating route pas-swagger-demo.cfapps.io...
OK

..

Showing health and status for app pas-swagger-demo in org apples-pivotal-org / space development as papicella@pivotal.io...
OK

requested state: started
instances: 1/1
usage: 1G x 1 instances
urls: pas-swagger-demo.cfapps.io
last uploaded: Wed Jun 14 03:32:31 UTC 2017
stack: cflinuxfs2
buildpack: container-certificate-trust-store=2.0.0_RELEASE java-buildpack=v3.15-offline-https://github.com/cloudfoundry/java-buildpack.git#a3a9e61 java-main java-opts open-jdk-like-jre=1.8.0_121 open-jdk-like-memory-calculator=2.0.2_RELEASE spring-auto-reconfigur...

     state     since                    cpu      memory         disk           details
#0   running   2017-06-14 01:33:40 PM   291.5%   510.9M of 1G   154.9M of 1G


The application is running on Pivotal Web Services as follows:

http://pas-swagger-demo.cfapps.io/swagger-ui.html



Monday, 22 May 2017

Using HTTPIE with Spring Boot Rest Repositories

I recently got introduced to HTTPIE as a command line alternative to CURL for testing RESTful api endpoints created using @RestController annotated classes. For more information on httpie follow this link

Before we test this out lets create a very basic Spring Boot Application with classes/interfaces to verify HTTPIE. The following assumes you have a Spring Boot application already created and it has maven dependancies as follows to enable JPA, Rest Repositories, H2 and Web support

Note: We are using Spring Boot 1.5.3 here

  
<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>1.5.3.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-rest</artifactId>
  </dependency>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-entitymanager</artifactId>
  </dependency>
  <dependency>
   <groupId>com.h2database</groupId>
   <artifactId>h2</artifactId>
   <scope>runtime</scope>
  </dependency>
 </dependencies>

1. Create classes/interfaces as follows

Employee.java
  
package pivotal.io.boot.httpie.demo;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Employee
{
    @Id
    @GeneratedValue (strategy = GenerationType.AUTO)
    private Long id;

    private String firstName;
    private String lastName;
    private String job;

    public Employee()
    {
    }

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

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

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

    public String getLastName() {
        return lastName;
    }

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

    public String getJob() {
        return job;
    }

    public void setJob(String job) {
        this.job = job;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                ", job='" + job + '\'' +
                '}';
    }
}

EmployeeRepository.java
  
package pivotal.io.boot.httpie.demo;

import org.springframework.data.jpa.repository.JpaRepository;

public interface EmployeeRepository extends JpaRepository <Employee, Long> {
}  

EmployeeRest.java
  
package pivotal.io.boot.httpie.demo;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping ("/api/employee")
public class EmployeeRest
{
    private static Log logger = LogFactory.getLog(EmployeeRest.class);

    @Autowired
    private EmployeeRepository employeeRepository;

    @GetMapping("/emps")
    public List<Employee> allEmployees()
    {
        return employeeRepository.findAll();
    }

    @GetMapping("/emp/{employeeId}")
    public Employee findEmployee (@PathVariable Long employeeId)
    {
        Employee emp = employeeRepository.findOne(employeeId);

        return emp;
    }

    @PostMapping("/emps")
    public Employee createEmployee(@RequestBody Employee employee)
    {
        return employeeRepository.save(employee);
    }

    @DeleteMapping("/emps/{employeeId}")
    public void deleteEmployee(@PathVariable Long employeeId)
    {
        Employee emp = employeeRepository.findOne(employeeId);
        employeeRepository.delete(emp);
        logger.info("Employee with id " + employeeId + " deleted...");
    }

}

2. Run the Spring Boot Application which will run on port localhost:8080


  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.5.3.RELEASE)

2017-05-22 13:39:22.910  INFO 8875 --- [           main] p.i.b.h.d.HttpieSpringbootApplication    : Starting HttpieSpringbootApplication on pas-macbook with PID 8875 (/Users/pasapicella/pivotal/DemoProjects/spring-starter/pivotal/httpie-springboot/target/classes started by pasapicella in /Users/pasapicella/pivotal/DemoProjects/spring-starter/pivotal/httpie-springboot)

...

2017-05-22 13:39:25.948  INFO 8875 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2017-05-22 13:39:25.952  INFO 8875 --- [           main] p.i.b.h.d.HttpieSpringbootApplication    : Started HttpieSpringbootApplication in 3.282 seconds (JVM running for 3.676)

Now we can test HTTPIE and here are some endpoints

3. Here are some examples with output

** All Employees **

pasapicella@pas-macbook:~$ http http://localhost:8080/api/employee/emps
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Date: Mon, 22 May 2017 01:26:43 GMT
Transfer-Encoding: chunked

[
    {
        "firstName": "pas",
        "id": 1,
        "job": "CEO",
        "lastName": "Apicella"
    },
    {
        "firstName": "lucia",
        "id": 2,
        "job": "CIO",
        "lastName": "Apicella"
    },
    {
        "firstName": "lucas",
        "id": 3,
        "job": "MANAGER",
        "lastName": "Apicella"
    },
    {
        "firstName": "siena",
        "id": 4,
        "job": "CLERK",
        "lastName": "Apicella"
    }
]

** Find Employee by {employeeId} **

pasapicella@pas-macbook:~$ http http://localhost:8080/api/employee/emp/1
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Date: Mon, 22 May 2017 01:31:32 GMT
Transfer-Encoding: chunked

{
    "firstName": "pas",
    "id": 1,
    "job": "CEO",
    "lastName": "Apicella"
}

** POST new employee **

pasapicella@pas-macbook:~$ http POST http://localhost:8080/api/employee/emps firstName=john lastName=black job=CLERK
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Date: Mon, 22 May 2017 02:32:34 GMT
Transfer-Encoding: chunked

{
    "firstName": "john",
    "id": 5,
    "job": "CLERK",
    "lastName": "black"
}

** POST with updated employee object **

pasapicella@pas-macbook:~$ http POST http://localhost:8080/api/employee/emps id:=5 firstName=john lastName=black job=CLEANER
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Date: Mon, 22 May 2017 02:36:06 GMT
Transfer-Encoding: chunked

{
    "firstName": "john",
    "id": 5,
    "job": "CLEANER",
    "lastName": "black"
}

** Delete employee with {employeeId} 5 **

pasapicella@pas-macbook:~$ http DELETE http://localhost:8080/api/employee/emps/5
HTTP/1.1 200
Content-Length: 0
Date: Mon, 22 May 2017 02:36:56 GMT

Tuesday, 2 May 2017

Binding a Spring Cloud Task to a Pivotal Cloud Foundry Database Service

I previously blogged about how to create and deploy a Spring Cloud Task to Pivotal Cloud Foundry (PCF) as shown below.

http://theblasfrompas.blogspot.com.au/2017/03/run-spring-cloud-task-from-pivotal.html

Taking that same example I have used the Spring Cloud Connectors to persist the log output to a database table to avoid looking through log files to view the output. Few things have to change to make this happen as detailed below.

1. We need to change the manifest.yml to include a MySQL service instance as shown below

applications:
- name: springcloudtask-date
  memory: 750M
  instances: 1
  no-route: true
  health-check-type: none
  path: ./target/springcloudtasktodaysdate-0.0.1-SNAPSHOT.jar
  services:
    - pmysql-test
  env:
    JAVA_OPTS: -Djava.security.egd=file:///dev/urando

2. Alter the project dependancies to include Spring Data JPA libraries to persist the log output to a table. Spring Cloud Connectors will automatically pick up the bound MySQL instance and connect for us when we push the application to PCF

https://github.com/papicella/SpringCloudTaskTodaysDate

  
<dependencies>
  <dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-task</artifactId>
  </dependency>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-entitymanager</artifactId>
  </dependency>
  <dependency>
   <groupId>com.h2database</groupId>
   <artifactId>h2</artifactId>
  </dependency>
  <dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <scope>runtime</scope>
  </dependency>
 </dependencies>

3. A Entity class, Spring JPA repository interface and a JPA task Configurer has been created for persisting the log output as shown in the code below.

TaskRunOutput.java
  
package pas.au.pivotal.pa.sct.demo;

import javax.persistence.*;

@Entity
@Table (name = "TASKRUNOUTPUT")
public class TaskRunOutput
{
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String output;

    public TaskRunOutput()
    {
    }

    public TaskRunOutput(String output) {
        this.output = output;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getOutput() {
        return output;
    }

    public void setOutput(String output) {
        this.output = output;
    }

    @Override
    public String toString() {
        return "TaskRunOutput{" +
                "id=" + id +
                ", output='" + output + '\'' +
                '}';
    }
}

TaskRepository.java
  
package pas.au.pivotal.pa.sct.demo;

import org.springframework.data.jpa.repository.JpaRepository;

public interface TaskRepository extends JpaRepository <TaskRun, Long>
{
}

JpaTaskConfigurer.java
  
package pas.au.pivotal.pa.sct.demo.configuration;

import java.text.SimpleDateFormat;
import java.util.Date;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import pas.au.pivotal.pa.sct.demo.TaskRunOutput;
import pas.au.pivotal.pa.sct.demo.TaskRunRepository;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.task.configuration.DefaultTaskConfigurer;
import org.springframework.cloud.task.listener.annotation.BeforeTask;
import org.springframework.cloud.task.repository.TaskExecution;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;

@Component
public class JpaTaskConfigurer extends DefaultTaskConfigurer {
 private static final Log logger = LogFactory.getLog(JpaTaskConfigurer.class);

 @Autowired
 private PlatformTransactionManager transactionManager;

 @Autowired
 private TaskRunRepository taskRunRepository;

 @Override
 public PlatformTransactionManager getTransactionManager() {
  if(this.transactionManager == null) {
   this.transactionManager = new JpaTransactionManager();
  }

  return this.transactionManager;
 }

 @BeforeTask
 public void init(TaskExecution taskExecution)
 {
  String execDate = new SimpleDateFormat().format(new Date());
  taskRunRepository.save(new TaskRunOutput("Executed at " + execDate));
  logger.info("Executed at : " + execDate);
 }
}

4. Now as per the previous blog execute the task and verify it completes without error. The screen shot below shows how the "Tasks" tab shows this

Note: You would need to PUSH the application to Pivotal Cloud Foundry before you can execute it which is shown on the original blog entry


5. Now if you follow this blog entry below you can deploy a Web Based interface for Pivotal MySQL instance to view the table and it's output

http://theblasfrompas.blogspot.com.au/2017/04/accessing-pivotal-mysql-service.html

With Pivotal MySQL*Web installed the output can be viewed as shown below.