Skip navigation

 

This blog post demonstrates usage of Oracle Application Container Cloud and Database Cloud service. To be precise, it covers the following

 

  • An introduction to Service bindings (in Application Container Cloud) including setup + configuration and leveraging them to integrate with Oracle Database Cloud service
  • Developing a sample (Java SE based) application using JPA (Eclipselink implementation) for persistence and JAX-RS (Jersey framework) to expose a REST API
  • Build, package and deploy the solution to the Application Container cloud using its REST APIs

 

 

 

 

The table below lists the components used

 

Component/service name

Description

 

 

Oracle Application Container Cloud

The aPaaS solution which hosts the Fat JAR based Java application exposing REST APIs

Oracle Database Cloud

hosts the application data

Oracle Storage Cloud

stores the application zip (for deployment)

Eclipselink

used as the JPA implementation (v 2.5.2)

Jersey

JAX-RS implementation (v 2.23.2)

Maven

Build tool. Makes use of the shade plugin to create a Fat JAR packaged with all dependent libraries

 

 

 

 

Service bindings: the concept

 

Service bindings serve as references to other Oracle Cloud services. At their core, they are a set of environment variables for a particular cloud service which are automatically seeded once you configure them. You can refer to these variables from within your application code. For example

 

String port = Optional.ofNullable(System.getenv("PORT")).orElse("8080"); //PORT is the environment variable

 

At the time of writing of this blog post, integration with following services are supported as far as service bindings are concerned - Oracle Database Cloud, Oracle Java Cloud and Oracle MySQL Cloud

 

What purpose do service bindings solve?

 

Service bindings make lives easier for developers

 

  • It's possible to consume other Oracle Cloud services in a declarative fashion
  • They allow secure storage of credentials required to access the service
  • Connection details are conveniently stored as environment variables (de facto standard) and can be easily used within code. This in turn, shields you from hard coding connectivity information or using/building a custom mechanism to handle these concerns

 

How to configure them?

 

Service binding configuration can be configured in a couple of ways

 

  • Metadata file (deployment.json) - It is possible to use this method both during as well as post application deployment
  • Application Container Cloud console - this is possible only post application deployment i.e. the application specific menu exposes this feature

 

 

We will use option #1 in the sample presented in this blog and will be covered in depth later

 

About the sample

 

Pre-requisites

 

You need to have access to the below mentioned Oracle Cloud Platform services to execute this sample end to end. Please refer to the links to find out more with regards to procuring service instances

 

 

An Oracle Storage Cloud account is automatically  provisioned along with your Application Container Cloud instance

Database Cloud service needs to be in the same identity domain as the Application Container Cloud for it to be available as a service binding

Architecture

 

The sample application presented in this blog is not complicated, yet, it makes sense to grasp the high level details with the help of a diagram

 

 

 

  • As already mentioned, the application leverages JPA (DB persistence) and JAX-RS (RESTful) APIs
  • The client invokes a HTTP(s) URL (GET request) which internally calls the JAX-RS resource, which in turn invokes the JPA (persistence) layer to communicate with Oracle Database Cloud instance
  • Connectivity to the Oracle Database Cloud instance is achieved with the help of service bindings which expose database connectivity details as environment variables
  • These variables are used within the code

 

Persistence (JPA) layer

 

Here is a summary of the JPA piece. Eclipselink is used as the JPA implementation and the sample makes use of specific JPA 2.1 features like automated schema creation and data seeding during application bootstrap phase

The table and its associated data will be created in Oracle Database cloud during application deployment phase. This approach been used on purpose in order to make this easy for you to test the application. It’s possible to manually bootstrap your Oracle Database Cloud instance with the table and some test data. You can turn off this feature by commenting out the highlighted line from persistence.xml

 

 

 

 

 

 

The important classes/components are as follows

 

Name

Description

 

 

                             persistence.xml

JPA deployment descriptor

PaasAppDev.java

JPA entity class

JPAFacade.java

Manages EntityManagerFactory life cycle and provides access to EntityManager

 

PaasAppDev.java

 

/**
 * JPA entity
 * 
 */
@Entity
@Table(name = "PAAS_APPDEV_PRODUCTS")
@XmlRootElement
public class PaasAppDev implements Serializable {


    @Id
    private String name;


    @Column(nullable = false)
    private String webUrl;


    public PaasAppDev() {
        //for JPA
    }
//getters & setters ommitted
}

 

JPAFacade.java

 

public class JPAFacade {


    private static EntityManagerFactory emf;


    private JPAFacade() {
    }


    public static void bootstrapEMF(String persistenceUnitName, Map<String, String> props) {
        if (emf == null) {
            emf = Persistence.createEntityManagerFactory(persistenceUnitName, props);
            emf.createEntityManager().close(); //a hack to initiate 'eager' deployment of persistence unit during deploy time as opposed to on-demand
        }
    }


    public static EntityManager getEM() {
        if (emf == null) {
            throw new IllegalStateException("Please call bootstrapEMF(String persistenceUnitName, Map<String, String> props) first");
        }


        return emf.createEntityManager();
    }


    public static void closeEMF() {


        if (emf == null) {
            throw new IllegalStateException("Please call bootstrapEMF(String persistenceUnitName, Map<String, String> props) first");
        }


        emf.close();


    }


}

 

 

 

Leveraging service binding information

 

It’s important to note how the service bindings are being used in this case. Generally, in case of standalone (with RESOURCE_LOCAL transactions) JPA usage, the DB connectivity information is stored as a part of the persistence.xml. In our sample, we are using programmatic configuration of EntityManagerFactory because the DB connection info can be extracted only at runtime using the following environment variables

 

  • DBAAS_DEFAULT_CONNECT_DESCRIPTOR
  • DBAAS_USER_NAME
  • DBAAS_USER_PASSWORD

 

This is leveraged in Bootstrap.java (which serves as the entry point to the applicaiton)

 

/**
 * The 'bootstrap' class. Sets up persistence and starts Grizzly HTTP server
 *
 */
public class Bootstrap {


    static void bootstrapREST() throws IOException {


        String hostname = Optional.ofNullable(System.getenv("HOSTNAME")).orElse("localhost");
        String port = Optional.ofNullable(System.getenv("PORT")).orElse("8080");


        URI baseUri = UriBuilder.fromUri("http://" + hostname + "/").port(Integer.parseInt(port)).build();


        ResourceConfig config = new ResourceConfig(PaasAppDevProductsResource.class)
                                                    .register(MoxyJsonFeature.class);


        HttpServer server = GrizzlyHttpServerFactory.createHttpServer(baseUri, config);
        Logger.getLogger(Bootstrap.class.getName()).log(Level.INFO, "Application accessible at {0}", baseUri.toString());


        //gracefully exit Grizzly and Eclipselink services when app is shut down
        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
            @Override
            public void run() {
                Logger.getLogger(Bootstrap.class.getName()).info("Exiting......");
                server.shutdownNow();
                JPAFacade.closeEMF();
                Logger.getLogger(Bootstrap.class.getName()).info("REST and Persistence services stopped");
            }
        }));
        server.start();


    }


    private static final String PERSISTENCE_UNIT_NAME = "oracle-cloud-db-PU";


    static void bootstrapJPA(String puName, Map<String, String> props) {


        JPAFacade.bootstrapEMF(puName, props);
        Logger.getLogger(Bootstrap.class.getName()).info("EMF bootstrapped");


    }


    public static void main(String[] args) throws IOException {
        Map<String, String> props = new HashMap<>();
        props.put("javax.persistence.jdbc.url", "jdbc:oracle:thin:@" + System.getenv("DBAAS_DEFAULT_CONNECT_DESCRIPTOR"));
        props.put("javax.persistence.jdbc.user", System.getenv("DBAAS_USER_NAME"));
        props.put("javax.persistence.jdbc.password", System.getenv("DBAAS_USER_PASSWORD"));
        bootstrapREST();
        bootstrapJPA(PERSISTENCE_UNIT_NAME, props);


    }
}

 

 

REST (JAX-RS) layer

 

Jersey is used as the JAX-RS implementation. It has support for multiple containers - Grizzly being one of them and it’s used in this example as well. Also, the Moxy media provider is leveraged in order to ensure that JAXB annotated (JPA) entity class can be marshaled as both XML and JSON without any additional code

 

Important classes

 

Name

Description

 

 

PaasAppDevProductsResource.java

Contains logic to GET information about all (appdev/products) or a specific PaaS product (e.g. appdev/products/ACC)

 

PaasAppDevProductsResource.java

 

@Path("appdev/products")
public class PaasAppDevProductsResource {


    @GET
    @Path("{name}")
    public Response paasOffering(@PathParam("name") String name) {


        EntityManager em = null;
        PaasAppDev product = null;
        try {
            em = JPAFacade.getEM();
            product = em.find(PaasAppDev.class, name);
        } catch (Exception e) {
            throw e;
        } finally {


            if (em != null) {
                em.close();
            }


        }
        
        return Response.ok(product).build();
    }
    
    @GET
    public Response all() {


        EntityManager em = null;
        List<PaasAppDev> products = null;
        try {
            em = JPAFacade.getEM();
            products = em.createQuery("SELECT c FROM PaasAppDev c").getResultList();
        } catch (Exception e) {
            throw e;
        } finally {


            if (em != null) {
                em.close();
            }


        }
        GenericEntity<List<PaasAppDev>> list = new GenericEntity<List<PaasAppDev>>(products) {
        };
        return Response.ok(list).build();
    }


}

Build & cloud deployment

 

Now that you have a fair idea of the application, it’s time to look at the build, packaging & deployment

 

Seed Maven with ojdbc7 driver JAR

 

 

mvn install:install-file -DgroupId=com.oracle -DartifactId=ojdbc7 -Dversion=12.1.0.1 -Dpackaging=jar -Dfile=<download_path>\ojdbc7.jar -DgeneratePom=true

 

Here is a snippet from the pom.xml

 

 

 

Metadata files

 

The manifest.json

 

You can use the manifest.json file as it is

 

{
    "runtime": {
        "majorVersion": "8"
    },
    "command": "java -jar accs-dbcs-service-binding-sample-1.0.jar",
    "release": {
        "build": "27092016.1020",
        "commit": "007",
        "version": "0.0.2"
    },
    "notes": "notes related to release"
}

 

Service bindings in deployment.json

 

The deployment.json file should contain your service bindings and you would need to upload this file during deployment (explained below) for them to be associated with your Application Container cloud instance.

 

{
    "services": [
    {
        "identifier": "DBService",
        "type": "DBAAS",
        "name": <Oracle DB Cloud service name>,
        "username": <Oracle DB Cloud username>,
        "password": <Oracle DB Cloud password>
    }]
}

 

 

You need to replace the placeholders with the appropriate values. Here is an example

 

{
    "services": [
    {
        "identifier": "OraDBService",
        "type": "DBAAS",
        "name": OracleCloudTestDB,
        "username": db_user,
        "password": Foo@Bar_007
    }]
}

 

 

In case of multiple service bindings for the same service (e.g. Java Cloud), the Application Container Cloud service automatically generates a unique set of environment variables for each service instance

 

Please refer to the following documentation if you need further details

 

Build & zip

 

Build JAR and zip it with (only) the manifest.json file to create a cloud-ready artifact

 

cd <code_dir> 
mvn clean install
zip accs-dbcs-service-binding-sample.zip manifest.json target\accs-dbcs-service-binding-sample-1.0.jar

 

 

Upload application zip to Oracle Storage cloud

 

You would first need to upload your application ZIP file to Oracle Storage Cloud and then reference it later. Here are the steps along with the cURL commands

Please refer to the following documentation for more details

 

Get authentication token for Oracle Storage cloud

 

you will receive the token in the HTTP Response header and you can use it to execute subsequent operations

 

curl -X GET -H "X-Storage-User: Storage-<identity_domain>:<user_name>" -H "X-Storage-Pass: <user_password>" "storage_cloud_URL" //template

curl -X GET -H "X-Storage-User: Storage-domain007:john.doe" -H "X-Storage-Pass: foo@bar" "https://domain007.storage.oraclecloud.com/auth/v1.0" //example

 

Create a container in Oracle Storage cloud (if it doesn't already exist)

 

curl -X PUT -H "X-Auth-Token: <your_auth_token>" "<storage_cloud_container_URL>" //template

curl -X PUT -H "X-Auth-Token: AUTH_foobaar007" "https://domain007.storage.oraclecloud.com/v1/Storage-domain007/accscontainer/" //example

 

Upload your zip file into the container (zip file is nothing but a Storage Cloud object)

 

curl -X PUT -H "X-Auth-Token: <your_auth_token>" -T <zip_file> "<storage_cloud_object_URL>" //template

curl -X PUT -H "X-Auth-Token: AUTH_foobaar007" -T accs-dbcs-service-binding-sample.zip "https://domain007.storage.oraclecloud.com/v1/Storage-domain007/accscontainer/accs-dbcs-service-binding-sample.zip" //example

 

Things to note

  • the <zip_file> is the application zip file which needs to uploaded and should be present on your file system from where you're executing these commands
  • the (storage cloud) object name needs to end with .zip extension (in this context/case)

 

Deploy to Application Container Cloud

 

Once you have finished uploading the ZIP, you can now reference its (Oracle Storage cloud) path while using the Application Container Cloud REST API which you would use in order to deploy the application. Here is a sample cURL command which makes use of the REST API

 

curl -X POST -u joe@example.com:password \
-H "X-ID-TENANT-NAME:domain007" \
-H "Content-Type: multipart/form-data" -F "name=accs-dbcs-service-binding-sample" \
-F "runtime=java" -F "subscription=Monthly" \
-F "deployment=@deployment.json" \
-F "archiveURL=accscontainer/accs-dbcs-service-binding-sample.zip" \
-F "notes=notes for deployment" \
https://apaas.oraclecloud.com/paas/service/apaas/api/v1.1/apps/domain007

 

 

During this process, you will also be pushing the deployment.json metadata file which contains the service bindings info (should be present on the file system on which the command is being executed). This in turn will automatically seed the required environment variables during application creation phase

 

More details available here

 

Test the application

 

Once deployment is successful, you should be able to see the deployed application and its details in Application Container cloud

 

 

 

Access your Oracle Database Cloud service

 

You can use Oracle SQL developer or similar client to confirm that the required table has been created and seeded with some test data

 

Details on how to configure your Oracle Database Cloud instance to connect via external tool (like SQL Developer) is out of scope of this article, but you can follow the steps outlined in the official documentation to set things up quickly

 

 

Access the REST endpoints

 

 

Purpose

Command

 

 

GET all products

curl -H "Accept: application/json" https://<application_URL>/appdev/products

GET a specific product

 

curl -H "Accept: application/json" https://<application_URL>/appdev/products/<product_name>

 

e.g. curl -H "Accept: application/json" https://<application_URL>/appdev/products/ACC

 

Refer above image for all product names

 

 

Please use the below mentioned application URL formats

 

https://accs-dbcs-service-binding-sample-<identity_domain>.apaas.<region>.oraclecloud.com/  //template

https://accs-dbcs-service-binding-sample-domain007.apaas.us2.oraclecloud.com/  //example

 

 

**The views expressed in this post are my own and do not necessarily reflect the views of Oracle.