Tuesday, 21 January 2014

Pivotal Cloud Foundry (PCF) binding a User Defined service for Oracle 11g r2

In this example below I show how you would create a user defined service for Oracle 11g R2 which is running outside of the PCF instance and how your applications would bind to that service. The example below is from the spring-music sample application which is a spring based web application.

https://github.com/cloudfoundry-samples/spring-music

1. First lets create our user defined service for Oracle , here we just use a simple URI which the Spring Cloud library knows how to use.

c:\pivotal\spring-music-oracle>cf create-service user-provided
Name?> oracle11gr2

What credential parameters should applications use to connect to this service instance?
(e.g. hostname, port, password)> uri

uri> oracle://scott:tiger@192.168.110.209:1521/XE

Creating service oracle11gr2... OK

c:\pivotal\spring-music-oracle>


The spring cloud project is here.

https://github.com/spring-projects/spring-cloud

2. Now with a previously created WAR file lets push the spring music app to PCF and bind to our oracle service as shown below.

c:\pivotal\spring-music-oracle>cf push --path=spring-music-oracle.war
Name> spring-music-oracle

Instances> 1

1: 128M
2: 256M
3: 512M
4: 1G
Memory Limit> 3

Creating spring-music-oracle... OK

1: spring-music-oracle
2: none
Subdomain> spring-music-oracle

1: system.com
2: none
Domain> system.com

Binding spring-music-oracle.system.com to spring-music-oracle... OK

Create services for application?> n

Bind other services to application?> y

1: mysql-dev
2: rmq
3: user-provided-705bf
4: oracle11gr2
Which service?> 4

Binding oracle11gr2 to spring-music-oracle... OK
Bind another service?> n

Save configuration?> n

Uploading spring-music-oracle... OK
Preparing to start spring-music-oracle... OK
-----> Downloaded app package (22M)
-----> Downloading OpenJDK 1.7.0_51 from http://download.pivotal.io.s3.amazonaws.com/openjdk/lucid/x86_64/openjdk-1.7.0_
51.tar.gz (1m 52s)
       Expanding JRE to .java (2.4s)
-----> Downloading Spring Auto-reconfiguration 0.8.4 from http://download.pivotal.io.s3.amazonaws.com/auto-reconfigurati
on/auto-reconfiguration-0.8.4.jar (4.3s)
       Modifying /WEB-INF/web.xml for Auto Reconfiguration
-----> Downloading Tomcat 7.0.50 from http://download.pivotal.io.s3.amazonaws.com/tomcat/tomcat-7.0.50.tar.gz (27.9s)
       Expanding Tomcat to .tomcat (0.3s)
-----> Downloading Buildpack Tomcat Support 1.1.1 from http://download.pivotal.io.s3.amazonaws.com/tomcat-buildpack-supp
ort/tomcat-buildpack-support-1.1.1.jar (0.2s)
-----> Uploading droplet (63M)
Checking status of app 'spring-music-oracle'...
  0 of 1 instances running (1 starting)
  0 of 1 instances running (1 starting)
  0 of 1 instances running (1 starting)
  0 of 1 instances running (1 starting)
  0 of 1 instances running (1 starting)
  0 of 1 instances running (1 starting)
  0 of 1 instances running (1 starting)
  0 of 1 instances running (1 starting)
  0 of 1 instances running (1 starting)
  0 of 1 instances running (1 starting)
  0 of 1 instances running (1 starting)
  1 of 1 instances running (1 running)
Push successful! App 'spring-music-oracle' available at spring-music-oracle.system.com

c:\pivotal\spring-music-oracle>


3. Here are some screenshots showing it deployed , invoking the application, and data within the Oracle 11g r2 schema.






The code to make this happen for Oracle is as follows, we do this to make use of the spring cloud code to bind to the user defined service "oracle11gr2" we created above. The spring cloud Library enables cloud applications to connect to services. The classes below show how we enable our oracle service for the spring music application.

OracleServiceInfo.java
  
package org.cloudfoundry.samples.music.cloud;

import org.springframework.cloud.service.ServiceInfo.ServiceLabel;
import org.springframework.cloud.service.common.RelationalServiceInfo;

@ServiceLabel("oracle")
public class OracleServiceInfo extends RelationalServiceInfo {

    public OracleServiceInfo(String id, String url) 
    {       
     super(id, url, "oracle");
    }

 @Override
 public String getJdbcUrl() 
 {
  return String.format("jdbc:%s:thin:%s/%s@%s:%d/%s",
                              "oracle",
                              super.getUserName(), super.getPassword(),
                              super.getHost(), super.getPort(), super.getPath());
 }
      
} 

OracleDataSourceCreator.java
  
package org.cloudfoundry.samples.music.cloud;

import org.cloudfoundry.samples.music.cloud.OracleServiceInfo;
import org.springframework.cloud.service.relational.DataSourceCreator;

public class OracleDataSourceCreator extends DataSourceCreator<OracleServiceInfo> {
    private static final String[] DRIVERS = new String[]{"oracle.jdbc.OracleDriver"};

    private static final String VALIDATION_QUERY = "SELECT 'Y' from dual";

    public OracleDataSourceCreator() {
     super("spring-cloud.oracle.driver", DRIVERS, VALIDATION_QUERY);
    }
} 

UserProvidedOracleServiceInfoCreator.java
  
package org.cloudfoundry.samples.music.cloud;

import org.springframework.cloud.cloudfoundry.CloudFoundryServiceInfoCreator;

import java.util.Map;

public class UserProvidedOracleServiceInfoCreator extends CloudFoundryServiceInfoCreator<OracleServiceInfo> {
    public UserProvidedOracleServiceInfoCreator() {
        super("user-provided");
    }

    @Override
    @SuppressWarnings("unchecked")
    public boolean accept(Map<String, Object> serviceData) {
        String label = (String) serviceData.get("label");

        if (label.equals(getTag())) {
            Map<String, Object> credentials = (Map<String, Object>) serviceData.get("credentials");
            String uri = (String) credentials.get("uri");

            if (uri != null && uri.startsWith("oracle:")) {
                return true;
            }
        }

        return false;
    }

    @Override
    @SuppressWarnings("unchecked")
    public OracleServiceInfo createServiceInfo(Map<String, Object> serviceData) {
        String id = (String) serviceData.get("name");
        Map<String, Object> credentials = (Map<String, Object>) serviceData.get("credentials");
        String uri = credentials.get("uri").toString();
        return new OracleServiceInfo(id, uri);
    }
}  

OracleRepositoryConfig.java
  
package org.cloudfoundry.samples.music.config.data;

import org.hibernate.dialect.MySQL5Dialect;
import org.hibernate.dialect.Oracle10gDialect;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@Configuration
@Profile("oracle")
@EnableJpaRepositories("org.cloudfoundry.samples.music.repositories.jpa")
public class OracleRepositoryConfig extends AbstractJpaRepositoryConfig {

    protected String getHibernateDialect() {
        return Oracle10gDialect.class.getName();
    }

}

RelationalCloudDataSourceConfig.java
  
package org.cloudfoundry.samples.music.config.data;

import org.springframework.cloud.config.java.AbstractCloudConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

import javax.sql.DataSource;

@Configuration
@Profile({"mysql-cloud", "postgres-cloud", "oracle-cloud"})
public class RelationalCloudDataSourceConfig extends AbstractCloudConfig {

    @Bean
    public DataSource dataSource() {
        return connectionFactory().dataSource();
    }

} 

src/main/resources/META-INF/servicesorg.springframework.cloud.cloudfoundry.CloudFoundryServiceInfoCreator

org.cloudfoundry.samples.music.cloud.UserProvidedMysqlServiceInfoCreator
org.cloudfoundry.samples.music.cloud.UserProvidedOracleServiceInfoCreator

src/main/resources/META-INF/services/org.springframework.cloud.service.ServiceConnectorCreator

org.springframework.cloud.service.relational.MysqlDataSourceCreator
org.cloudfoundry.samples.music.cloud.OracleDataSourceCreator
org.springframework.cloud.service.relational.PostgresqlDataSourceCreator
org.springframework.cloud.service.keyval.RedisConnectionFactoryCreator
org.springframework.cloud.service.document.MongoDbFactoryCreator
org.springframework.cloud.service.messaging.RabbitConnectionFactoryCreator
org.springframework.cloud.service.smtp.MailSenderCreator

Finally you need to ensure that ojdbc6.jar is shipped with the WAR file within "WEB-INF/lib" which is the Oracle JDBC driver to enable the application to connect to oracle.

More Information on Pivotal CF

http://www.gopivotal.com/products/cloud-foundry



No comments: