简体   繁体   中英

Play Framework unable to read environment variable in Cloud Foundry

I am trying to read connection URL environment variable of PostgreSQL service inside my application.conf as follows:

db.default.driver="org.postgresql.Driver"
db.default.url=${?cloud.services.postgresql.connection.url}

My VCAP_SERVICES is as follows

{
  "postgresql": [
    {
      "binding_name": null,
      "credentials": {
        "dbname": "sample-db",
        "end_points": [
          {
            "host": "x.x.x.x",
            "network_id": "SF",
            "port": "44980"
          }
        ],
        "hostname": "x.x.x.x",
        "password": "sample-password",
        "port": "44980",
        "ports": {
          "5432/tcp": "44980"
        },
        "uri": "postgres://sample-user:sample-password@x.x.x.x:44980/sample-db",
        "username": "sample-user"
      },
      "instance_name": "postgresql",
      "label": "postgresql",
      "name": "postgresql",
      "plan": "v9.6-dev",
      "provider": null,
      "syslog_drain_url": null,
      "tags": [
        "postgresql",
        "relational"
      ],
      "volume_mounts": []
    }
  ]
}

I am following this article .

However the database won't configure and the root was Configuration error[jdbcUrl is required with driverClassName.] . Full exception dump below.

play.api.Configuration$$anon$1: Configuration error[Cannot initialize to database [default]]
    at play.api.Configuration$.configError(Configuration.scala:155)
    at play.api.Configuration.reportError(Configuration.scala:394)
    at play.api.db.DefaultDBApi.$anonfun$initialize$1(DefaultDBApi.scala:76)
    at scala.collection.immutable.List.foreach(List.scala:333)
    at play.api.db.DefaultDBApi.initialize(DefaultDBApi.scala:68)
    at play.api.db.DBApiProvider.get$lzycompute(DBModule.scala:92)
    at play.api.db.DBApiProvider.get(DBModule.scala:77)
    at play.api.db.DBApiProvider.get(DBModule.scala:59)
    at com.google.inject.internal.ProviderInternalFactory.provision(ProviderInternalFactory.java:85)
    at com.google.inject.internal.BoundProviderFactory.provision(BoundProviderFactory.java:77)
    at com.google.inject.internal.ProviderInternalFactory.circularGet(ProviderInternalFactory.java:59)
    at com.google.inject.internal.BoundProviderFactory.get(BoundProviderFactory.java:61)
    at com.google.inject.internal.SingleFieldInjector.inject(SingleFieldInjector.java:52)
    at com.google.inject.internal.MembersInjectorImpl.injectMembers(MembersInjectorImpl.java:147)
    at com.google.inject.internal.MembersInjectorImpl.injectAndNotify(MembersInjectorImpl.java:101)
    at com.google.inject.internal.MembersInjectorImpl.injectMembers(MembersInjectorImpl.java:71)
    at com.google.inject.internal.InjectorImpl.injectMembers(InjectorImpl.java:1055)
    at com.google.inject.util.Providers$GuicifiedProviderWithDependencies.initialize(Providers.java:154)
    at com.google.inject.util.Providers$GuicifiedProviderWithDependencies$$FastClassByGuice$$2a7177aa.invoke(<generated>)
    at com.google.inject.internal.SingleMethodInjector$1.invoke(SingleMethodInjector.java:51)
    at com.google.inject.internal.SingleMethodInjector.inject(SingleMethodInjector.java:85)
    at com.google.inject.internal.MembersInjectorImpl.injectMembers(MembersInjectorImpl.java:147)
    at com.google.inject.internal.MembersInjectorImpl.injectAndNotify(MembersInjectorImpl.java:101)
    at com.google.inject.internal.Initializer$InjectableReference.get(Initializer.java:245)
    at com.google.inject.internal.Initializer.injectAll(Initializer.java:140)
    at com.google.inject.internal.InternalInjectorCreator.injectDynamically(InternalInjectorCreator.java:178)
    at com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:111)
    at com.google.inject.Guice.createInjector(Guice.java:87)
    at com.google.inject.Guice.createInjector(Guice.java:78)
    at play.api.inject.guice.GuiceBuilder.injector(GuiceInjectorBuilder.scala:200)
    at play.inject.guice.GuiceBuilder.injector(GuiceBuilder.java:211)
    at play.inject.guice.GuiceApplicationBuilder.build(GuiceApplicationBuilder.java:121)
    at play.inject.guice.GuiceApplicationLoader.load(GuiceApplicationLoader.java:32)
    at play.api.ApplicationLoader$JavaApplicationLoaderAdapter$1.load(ApplicationLoader.scala:181)
    at play.core.server.DevServerStart$$anon$1.$anonfun$reload$3(DevServerStart.scala:190)
    at play.utils.Threads$.withContextClassLoader(Threads.scala:22)
    at play.core.server.DevServerStart$$anon$1.reload(DevServerStart.scala:182)
    at play.core.server.DevServerStart$$anon$1.get(DevServerStart.scala:142)
    at play.core.server.AkkaHttpServer.handleRequest(AkkaHttpServer.scala:301)
    at play.core.server.AkkaHttpServer.$anonfun$createServerBinding$1(AkkaHttpServer.scala:191)
    at akka.stream.impl.fusing.MapAsync$$anon$30.onPush(Ops.scala:1285)
    at akka.stream.impl.fusing.GraphInterpreter.processPush(GraphInterpreter.scala:541)
    at akka.stream.impl.fusing.GraphInterpreter.execute(GraphInterpreter.scala:423)
    at akka.stream.impl.fusing.GraphInterpreterShell.runBatch(ActorGraphInterpreter.scala:625)
    at akka.stream.impl.fusing.GraphInterpreterShell$AsyncInput.execute(ActorGraphInterpreter.scala:502)
    at akka.stream.impl.fusing.GraphInterpreterShell.processEvent(ActorGraphInterpreter.scala:600)
    at akka.stream.impl.fusing.ActorGraphInterpreter.akka$stream$impl$fusing$ActorGraphInterpreter$$processEvent(ActorGraphInterpreter.scala:769)
    at akka.stream.impl.fusing.ActorGraphInterpreter$$anonfun$receive$1.applyOrElse(ActorGraphInterpreter.scala:784)
    at akka.actor.Actor.aroundReceive(Actor.scala:535)
    at akka.actor.Actor.aroundReceive$(Actor.scala:533)
    at akka.stream.impl.fusing.ActorGraphInterpreter.aroundReceive(ActorGraphInterpreter.scala:691)
    at akka.actor.ActorCell.receiveMessage(ActorCell.scala:575)
    at akka.actor.ActorCell.invoke(ActorCell.scala:545)
    at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:270)
    at akka.dispatch.Mailbox.run(Mailbox.scala:231)
    at akka.dispatch.Mailbox.exec(Mailbox.scala:243)
    at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
    at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
    at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
    at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
Caused by: play.api.Configuration$$anon$1: Configuration error[jdbcUrl is required with driverClassName.]
    at play.api.Configuration$.configError(Configuration.scala:155)
    at play.api.Configuration.reportError(Configuration.scala:394)
    at play.api.db.HikariCPConnectionPool.create(HikariCPModule.scala:70)
    at play.api.db.PooledDatabase.createDataSource(Databases.scala:249)
    at play.api.db.DefaultDatabase.dataSource$lzycompute(Databases.scala:141)
    at play.api.db.DefaultDatabase.dataSource(Databases.scala:139)
    at play.api.db.DefaultDBApi.$anonfun$initialize$1(DefaultDBApi.scala:72)
    ... 57 common frames omitted
Caused by: java.lang.IllegalArgumentException: jdbcUrl is required with driverClassName.
    at com.zaxxer.hikari.HikariConfig.validate(HikariConfig.java:1000)
    at play.api.db.HikariCPConfig.toHikariConfig(HikariCPModule.scala:140)
    at play.api.db.HikariCPConnectionPool.$anonfun$create$1(HikariCPModule.scala:57)
    at scala.util.Try$.apply(Try.scala:210)
    at play.api.db.HikariCPConnectionPool.create(HikariCPModule.scala:54)
    ... 61 common frames omitted

Using Play Framework 2.8.

It looks like Cloud Foundry support for Play Framework has been broken since Play 2.5. Cloud Foundry's Java Buildpack removed support for it which in turn invalidates the corresponding documentation in Play Framework which refers to configuration keys prefixed by {?cloud.services....} .

I ended writing code to parse VCAP_SERVICES myself and insert it in the application loader, following the documentation to create your own application loader :

package com.example.config;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;

import play.ApplicationLoader;
import play.inject.guice.GuiceApplicationBuilder;
import play.inject.guice.GuiceApplicationLoader;


public class MyApplicationLoader extends GuiceApplicationLoader  {
    private static final Logger LOGGER = Logger.getLogger(MyApplicationLoader.class.getCanonicalName());

    @Override
    public GuiceApplicationBuilder builder(ApplicationLoader.Context context) {
        // https://www.programcreek.com/scala/play.api.Configuration
        // https://www.playframework.com/documentation/2.8.x/JavaDependencyInjection#Advanced:-Extending-the-GuiceApplicationLoader
        
        Config cloudConfig = parseCloudFoundryEnvironmentConfig(context);

        return initialBuilder
                    .in(context.environment())
                    .loadConfig(cloudConfig.withFallback(context.initialConfig()))
                    .overrides(overrides(context));
    }

    static Config parseCloudFoundryEnvironmentConfig(ApplicationLoader.Context context) {
        final ObjectMapper objectMapper = new ObjectMapper();
        final HashMap<String,Object> configOutput = new HashMap<>();
        
        final String vcap_services_str = System.getenv("VCAP_SERVICES");
        if(vcap_services_str != null) {
            try {
                JsonNode rootNode = objectMapper.readTree(vcap_services_str);
                /// ... parse VCAP_SERVICES and initialize the "db...." Play configuration into `configOutput`
            } catch(IOException ex) {
                LOGGER.log(Level.SEVERE, ex, () -> MessageFormat.format("Unable to parse VCAP_SERVICES content: {0}", vcap_services_str));
            }           
        } else {
            LOGGER.info("VCAP_SERVICES not defined");
        }
        Config configResult = ConfigFactory.parseMap(configOutput, "Environment Variables");
        return configResult;
    }
}

Then I activated this new application loader by creating an entry in the reference.conf configuration file under the conf folder.

play.application.loader=com.example.config.MyApplicationLoader

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM