diff --git a/apps/meta-app/src/main/resources/reference.conf b/apps/meta-app/src/main/resources/reference.conf index 31332d836..372b0c89f 100644 --- a/apps/meta-app/src/main/resources/reference.conf +++ b/apps/meta-app/src/main/resources/reference.conf @@ -1,25 +1,21 @@ shrine { metaData { ping = "pong" } - mom { - hornetq{ - serverUrl = "https://localhost:6443/shrine-metadata/mom" - } - } } //todo typesafe config precedence seems to do the right thing, but I haven't found the rules that say this reference.conf should override others +// todo go with the actor system, whcih we will pull out later akka { loglevel = INFO // log-config-on-start = on loggers = ["akka.event.slf4j.Slf4jLogger"] // logging-filter = "akka.event.slf4j.Slf4jLoggingFilter" // Toggles whether the threads created by this ActorSystem should be daemons or not daemonic = on } spray.servlet { boot-class = "net.shrine.metadata.Boot" request-timeout = 30s } diff --git a/messagequeue/hornetqclient/src/main/resources/reference.conf b/messagequeue/hornetqclient/src/main/resources/reference.conf index 2303e221d..232f1a047 100644 --- a/messagequeue/hornetqclient/src/main/resources/reference.conf +++ b/messagequeue/hornetqclient/src/main/resources/reference.conf @@ -1,18 +1,19 @@ shrine { - messagequeue { - hornetq { - serverUrl = "https://localhost:6443/shrine-metadata/mom" - } + messagequeue { + hornetq { + serverUrl = "https://localhost:6443/shrine-metadata/mom" } + } } //todo typesafe config precedence seems to do the right thing, but I haven't found the rules that say this reference.conf should override others +// todo go with the actor system, also check other apps, possiblly they use it too akka { loglevel = INFO // log-config-on-start = on loggers = ["akka.event.slf4j.Slf4jLogger"] // logging-filter = "akka.event.slf4j.Slf4jLoggingFilter" // Toggles whether the threads created by this ActorSystem should be daemons or not daemonic = on } \ No newline at end of file diff --git a/messagequeue/hornetqclient/src/test/resources/shrine.conf b/messagequeue/hornetqclient/src/test/resources/shrine.conf deleted file mode 100644 index e69de29bb..000000000 diff --git a/messagequeue/hornetqmom/src/main/resources/reference.conf b/messagequeue/hornetqmom/src/main/resources/reference.conf index 9ba15e537..540242046 100644 --- a/messagequeue/hornetqmom/src/main/resources/reference.conf +++ b/messagequeue/hornetqmom/src/main/resources/reference.conf @@ -1,7 +1,6 @@ shrine { messagequeue { hornetq { - serverUrl = "https://localhost:6443/shrine-metadata/mom" } } } \ No newline at end of file diff --git a/messagequeue/hornetqmom/src/main/scala/net/shrine/hornetqmom/LocalHornetQMom.scala b/messagequeue/hornetqmom/src/main/scala/net/shrine/hornetqmom/LocalHornetQMom.scala index 46e6d0fa7..19d6d8641 100644 --- a/messagequeue/hornetqmom/src/main/scala/net/shrine/hornetqmom/LocalHornetQMom.scala +++ b/messagequeue/hornetqmom/src/main/scala/net/shrine/hornetqmom/LocalHornetQMom.scala @@ -1,144 +1,145 @@ package net.shrine.hornetqmom import com.typesafe.config.Config import net.shrine.messagequeueservice.{Message, MessageQueueService, Queue} import net.shrine.source.ConfigSource import org.hornetq.api.core.client.{ClientConsumer, ClientMessage, ClientSession, ClientSessionFactory, HornetQClient, ServerLocator} import org.hornetq.api.core.management.HornetQServerControl import org.hornetq.api.core.{HornetQQueueExistsException, TransportConfiguration} import org.hornetq.core.config.impl.ConfigurationImpl import org.hornetq.core.remoting.impl.invm.{InVMAcceptorFactory, InVMConnectorFactory} import org.hornetq.core.server.{HornetQServer, HornetQServers} import scala.collection.immutable.Seq import scala.concurrent.blocking import scala.concurrent.duration.Duration import scala.collection.concurrent.{TrieMap, Map => ConcurrentMap} /** * This object is the local version of the Message-Oriented Middleware API, which uses HornetQ service * @author david * @since 7/18/17 */ object LocalHornetQMom extends MessageQueueService { val config:Config = ConfigSource.config.getConfig("shrine.messagequeue.hornetq") // todo use the config to set everything needed here that isn't hard-coded. val hornetQConfiguration = new ConfigurationImpl() // todo from config? What is the journal file about? If temporary, use a Java temp file. hornetQConfiguration.setJournalDirectory("target/data/journal") // todo want this. There are likely many other config bits hornetQConfiguration.setPersistenceEnabled(false) // todo maybe want this hornetQConfiguration.setSecurityEnabled(false) // todo probably just want the InVM version, but need to read up on options hornetQConfiguration.getAcceptorConfigurations.add(new TransportConfiguration(classOf[InVMAcceptorFactory].getName)) // Create and start the server val hornetQServer: HornetQServer = HornetQServers.newHornetQServer(hornetQConfiguration) hornetQServer.start() val serverLocator: ServerLocator = HornetQClient.createServerLocatorWithoutHA(new TransportConfiguration(classOf[InVMConnectorFactory].getName)) val sessionFactory: ClientSessionFactory = serverLocator.createSessionFactory() //arguments are boolean xa, boolean autoCommitSends, boolean autoCommitAcks . val session: ClientSession = sessionFactory.createSession(false,true,true) session.start() //keep a map of live queues to ClientConsumers to provide a path for completing messages val queuesToConsumers:ConcurrentMap[Queue, ClientConsumer] = TrieMap.empty val propName = "contents" /** * Use HornetQMomStopper to stop the hornetQServer without unintentially starting it */ + // todo drop this into a try private[hornetqmom] def stop() = { queuesToConsumers.values.foreach(_.close()) session.close() sessionFactory.close() hornetQServer.stop() } //queue lifecycle def createQueueIfAbsent(queueName:String):Queue = { val serverControl: HornetQServerControl = hornetQServer.getHornetQServerControl if(!queues.map(_.name).contains(queueName)) { try serverControl.createQueue(queueName, queueName, true) catch { case alreadyExists: HornetQQueueExistsException => //Already have what we want. Something slipped in between checking queues for this queue and creating it. } } val queue = Queue(queueName) queuesToConsumers.getOrElseUpdate(queue,{session.createConsumer(queue.name)}) queue } def deleteQueue(queueName:String) = { queuesToConsumers.remove(Queue(queueName)).foreach(_.close()) val serverControl: HornetQServerControl = hornetQServer.getHornetQServerControl serverControl.destroyQueue(queueName) } override def queues:Seq[Queue] = { val serverControl: HornetQServerControl = hornetQServer.getHornetQServerControl val queueNames: Array[String] = serverControl.getQueueNames queueNames.map(Queue(_)).to[Seq] } //send a message def send(contents:String,to:Queue):Unit = { val producer = session.createProducer(to.name) try { val message = session.createMessage(true) message.putStringProperty(propName, contents) producer.send(message) } finally { producer.close() } } //receive a message /** * Always do AWS SQS-style long polling. * Be sure your code can handle receiving the same message twice. * * @return Some message before the timeout, or None */ def receive(from:Queue,timeout:Duration):Option[Message] = { // todo check if queue exists, if not throw exception val messageConsumer: ClientConsumer = queuesToConsumers(from) //todo handle the case where either stop or close has been called on something gracefully blocking { val messageReceived: Option[ClientMessage] = Option(messageConsumer.receive(timeout.toMillis)) val message = messageReceived.map(Message(_)) message } } //todo dead letter queue for all messages. See http://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/examples-sqs-dead-letter-queues.html //complete a message //todo better here or on the message itself?? override def completeMessage(message:Message):Unit = message.complete() } /** * If the configuration is such that HornetQ should have been started use this object to stop it */ //todo is this a good way to write this code? object LocalHornetQMomStopper { def stop() = { //todo fill in as part of SHIRINE-2128 val config: Config = ConfigSource.config.getConfig("shrine.messagequeue.hornetq") LocalHornetQMom.stop() } } \ No newline at end of file diff --git a/messagequeue/messagequeueservice/src/main/resources/reference.conf b/messagequeue/messagequeueservice/src/main/resources/reference.conf deleted file mode 100644 index e69de29bb..000000000 diff --git a/messagequeue/messagequeueservice/src/main/scala/net/shrine/messagequeueservice/MessageQueueService.scala b/messagequeue/messagequeueservice/src/main/scala/net/shrine/messagequeueservice/MessageQueueService.scala index 1472edef7..4bd94f28f 100644 --- a/messagequeue/messagequeueservice/src/main/scala/net/shrine/messagequeueservice/MessageQueueService.scala +++ b/messagequeue/messagequeueservice/src/main/scala/net/shrine/messagequeueservice/MessageQueueService.scala @@ -1,62 +1,63 @@ package net.shrine.messagequeueservice import net.shrine.spray.DefaultJsonSupport import org.hornetq.api.core.client.ClientMessage import org.hornetq.core.client.impl.ClientMessageImpl import org.json4s.JsonAST.{JField, JObject} import org.json4s.{CustomSerializer, DefaultFormats, Formats, _} import scala.collection.immutable.Seq import scala.concurrent.duration.Duration /** * This object mostly imitates AWS SQS' API via an embedded HornetQ. See http://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/examples-sqs.html * * @author david * @since 7/18/17 */ //todo in 1.23 all but the server side will use the client RemoteHornetQ implementation (which will call to the server at the hub) //todo in 1.24, create an AwsSqs implementation of the trait trait MessageQueueService { def createQueueIfAbsent(queueName:String):Queue def deleteQueue(queueName:String) def queues:Seq[Queue] def send(contents:String,to:Queue):Unit def receive(from:Queue,timeout:Duration):Option[Message] def completeMessage(message:Message):Unit } case class Message(hornetQMessage:ClientMessage) extends DefaultJsonSupport { override implicit def json4sFormats: Formats = DefaultFormats val propName = "contents" def getClientMessage = hornetQMessage def contents = hornetQMessage.getStringProperty(propName) def getMessageID = hornetQMessage.getMessageID def complete() = hornetQMessage.acknowledge() } case class Queue(name:String) extends DefaultJsonSupport class MessageSerializer extends CustomSerializer[Message](format => ( { //JObject(List((hornetQMessage,JObject(List((type,JInt(0)), (durable,JBool(false)), (expiration,JInt(0)), (timestamp,JInt(1502218873012)), (priority,JInt(4))))))) // type, durable, expiration, timestamp, priority, initialMessageBufferSize case JObject(JField("hornetQMessage", JObject(JField("type", JInt(s)) :: JField("durable", JBool(d)) :: JField("expiration", JInt(e)) :: JField("timestamp", JInt(t)) :: JField("priority", JInt(p)) :: Nil)) :: Nil) => new Message(new ClientMessageImpl(s.toByte, d, e.toLong, t.toLong, p.toByte, 0)) }, { case msg: Message => JObject(JField("hornetQMessage", JObject(JField("type", JLong(msg.getClientMessage.getType)) :: JField("durable", JBool(msg.getClientMessage.isDurable)) :: JField("expiration", JLong(msg.getClientMessage.getExpiration)) :: JField("timestamp", JLong(msg.getClientMessage.getTimestamp)) :: JField("priority", JLong(msg.getClientMessage.getPriority)) :: Nil)) :: Nil) } -)) \ No newline at end of file +)) +// todo test MessageSerializer \ No newline at end of file diff --git a/messagequeue/messagequeueservice/src/test/resources/shrine.conf b/messagequeue/messagequeueservice/src/test/resources/shrine.conf deleted file mode 100644 index e69de29bb..000000000 diff --git a/shrine-setup/src/main/resources/shrine.conf b/shrine-setup/src/main/resources/shrine.conf index aed15d407..8402752c2 100644 --- a/shrine-setup/src/main/resources/shrine.conf +++ b/shrine-setup/src/main/resources/shrine.conf @@ -1,346 +1,352 @@ shrine { metaData { ping = "pong" } pmEndpoint { // url = "http://shrine-dev1.catalyst/i2b2/services/PMService/getServices" //use your i2b2 pm url } ontEndpoint { // url = "http://shrine-dev1.catalyst/i2b2/rest/OntologyService/" //use your i2b2 ontology url } hiveCredentials { //use your i2b2 hive credentials // domain = "i2b2demo" // username = "demo" // password = "examplePassword" // crcProjectId = "Demo" // ontProjectId = "SHRINE" } + messagequeue { + hornetq { + serverUrl = "https://localhost:6443/shrine-metadata/mom" + } + } + breakdownResultOutputTypes { //use breakdown values appropriate for your shrine network // PATIENT_AGE_COUNT_XML { // description = "Age patient breakdown" // } // PATIENT_RACE_COUNT_XML { // description = "Race patient breakdown" // } // PATIENT_VITALSTATUS_COUNT_XML { // description = "Vital Status patient breakdown" // } // PATIENT_GENDER_COUNT_XML { // description = "Gender patient breakdown" // } } queryEntryPoint { // create = true //false for no qep // audit { // collectQepAudit = true //false to not use the 1.20 audit db tables // database { // dataSourceFrom = "JNDI" //Can be JNDI or testDataSource . Use testDataSource for tests, JNDI everywhere else // jndiDataSourceName = "java:comp/env/jdbc/qepAuditDB" //or leave out for tests // slickProfileClassName = "slick.driver.MySQLDriver$" // Can be // slick.driver.H2Driver$ // slick.driver.MySQLDriver$ // slick.driver.PostgresDriver$ // slick.driver.SQLServerDriver$ // slick.driver.JdbcDriver$ // freeslick.OracleProfile$ // freeslick.MSSQLServerProfile$ // // (Yes, with the $ on the end) // For testing without JNDI // testDataSource { //typical test settings for unit tests //driverClassName = "org.h2.Driver" //url = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1" //H2 embedded in-memory for unit tests //url = "jdbc:h2:~/stewardTest.h2" //H2 embedded on disk at ~/test // } // timeout = 30 //time to wait before db gives up, in seconds. // createTablesOnStart = false //for testing with H2 in memory, when not running unit tests. Set to false normally // } // } // trustModelIsHub = true // true by default, false for P2P networks. // authenticationType = "pm" //can be none, pm, or ecommons // authorizationType = "shrine-steward" //can be none, shrine-steward, or hms-steward //hms-steward config // sheriffEndpoint { // url = "http://localhost:8080/shrine-hms-authorization/queryAuthorization" // timeout { // seconds = 1 // } // } // sheriffCredentials { // username = "sheriffUsername" // password = "sheriffPassword" // } //shrine-steward config // shrineSteward { // qepUserName = "qep" // qepPassword = "trustme" // stewardBaseUrl = "https://localhost:6443" // } // includeAggregateResults = false // // maxQueryWaitTime { // minutes = 5 //must be longer than the hub's maxQueryWaitTime // } // broadcasterServiceEndpoint { // url = "http://example.com/shrine/rest/broadcaster/broadcast" //url for the hub // acceptAllCerts = true // timeout { // seconds = 1 // } // } } hub { // create = false //change to true to start a hub maxQueryWaitTime { // minutes = 4.5 //Needs to be longer than the adapter's maxQueryWaitTime, but shorter than the qep's } // downstreamNodes { //Add your downstream nodes here // shrine-dev2 = "https://shrine-dev2.catalyst:6443/shrine/rest/adapter/requests" // } shouldQuerySelf = false //true if there is an adapter at the hub , or just add a loopback address to downstreamNodes } adapter { // create = true by default. False to not create an adapter. // audit { // collectAdapterAudit = true by default. False to not fill in the audit database // database { // dataSourceFrom = "JNDI" //Can be JNDI or testDataSource . Use testDataSource for tests, JNDI everywhere else // jndiDataSourceName = "java:comp/env/jdbc/adapterAuditDB" //or leave out for tests // slickProfileClassName = "slick.driver.MySQLDriver$" // Can be // slick.driver.H2Driver$ // slick.driver.MySQLDriver$ // slick.driver.PostgresDriver$ // slick.driver.SQLServerDriver$ // slick.driver.JdbcDriver$ // freeslick.OracleProfile$ // freeslick.MSSQLServerProfile$ // // (Yes, with the $ on the end) // For testing without JNDI // testDataSource { //typical test settings for unit tests //driverClassName = "org.h2.Driver" //url = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1" //H2 embedded in-memory for unit tests //url = "jdbc:h2:~/stewardTest.h2" //H2 embedded on disk at ~/test // } // createTablesOnStart = false //for testing with H2 in memory, when not running unit tests. Set to false normally // } // obfuscation { // binSize = 5 by default //Round to the nearest binSize. Use 1 for no effect (to match SHRINE 1.21 and earlier). // sigma = 6.5 by default //Noise to inject. Use 0 for no effect. (Use 1.33 to match SHRINE 1.21 and earlier). // clamp = 10 by default //Maximum ammount of noise to inject. (Use 3 to match SHRINE 1.21 and earlier). // } // adapterLockoutAttemptsThreshold = 0 by default // Number of allowed queries with the same actual result that can exist before a researcher is locked out of the adapter. In 1.24 the lockout code and this config value will be removed // botDefense { // countsAndMilliseconds = [ //to turn off, use an empty json list // {count = 10, milliseconds = 60000}, //allow up to 10 queries in one minute by default // {count = 200, milliseconds = 36000000} //allow up to 4 queries in 10 hours by default // ] // } crcEndpoint { //must be filled in url = "http://shrine-dev1.catalyst/i2b2/services/QueryToolService/" } setSizeObfuscation = true //must be set. false turns off obfuscation adapterMappingsFileName = "AdapterMappings.xml" maxSignatureAge { minutes = 5 //must be longer than the hub's maxQueryWaitTime } immediatelyRunIncomingQueries = true //false to queue them //delayResponse = "0 seconds" //time to delay before responding to a query. Should be 0 except for testing in shrine-qa } networkStatusQuery = "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\" humanReadableNodeName = "shrine-dev1" shrineDatabaseType = "mysql" keystore { file = "/opt/shrine/shrine.keystore" password = "changeit" privateKeyAlias = "shrine-dev1.catalyst" keyStoreType = "JKS" caCertAliases = [ "shrine-dev-ca" ] } problem { // problemHandler = "net.shrine.problem.LogAndDatabaseProblemHandler$" Can be other specialized problemHandler implementations // database { // dataSourceFrom = "JNDI" //Can be JNDI or testDataSource . Use testDataSource for tests, JNDI everywhere else // jndiDataSourceName = "java:comp/env/jdbc/problemDB" // slickProfileClassName = "slick.driver.MySQLDriver$" // Can be // slick.driver.H2Driver$ // slick.driver.MySQLDriver$ // slick.driver.PostgresDriver$ // slick.driver.SQLServerDriver$ // slick.driver.JdbcDriver$ // freeslick.OracleProfile$ // freeslick.MSSQLServerProfile$ // // (Yes, with the $ on the end) // For testing without JNDI // testDataSource { //typical test settings for unit tests //driverClassName = "org.h2.Driver" //url = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1" //H2 embedded in-memory for unit tests //url = "jdbc:h2:~/stewardTest.h2" //H2 embedded on disk at ~/test // } // createTablesOnStart = false //for testing with H2 in memory, when not running unit tests. Set to false normally // } } dashboard { // gruntWatch = false //false for production, true for mvn tomcat7:run . Allows the client javascript and html files to be loaded via gruntWatch . // happyBaseUrl = "https://localhost:6443/shrine/rest/happy" If the shine servlet is running on a different machime from the dashboard, change this URL to match // statusBaseUrl = "https://localhost:6443/shrine/rest/internalstatus" If the shine servlet is running on a different machime from the dashboard, change this URL to match // } // status { //permittedHostOfOrigin = "localhost" //If absent then get the host name via java.net.InetAddress.getLocalHost.getHostName . Override to control } //Get the older squerl-basd databases through JNDI (inside of tomcant, using tomcat's db connection pool) or directly via a db config here (for testing squerylDataSource { // database { // dataSourceFrom = "JNDI" //Can be JNDI or testDataSource . Use testDataSource for tests, JNDI everywhere else // jndiDataSourceName = "java:comp/env/jdbc/shrineDB" //or leave out for tests // } } authenticate { // realm = "SHRINE Researcher API" //todo figure out what this means. SHRINE-1978 usersource { // domain = "i2b2demo" //you must provide your own domain } } steward { // createTopicsMode = Pending //Can be Pending, Approved, or TopcisIgnoredJustLog. Pending by default //Pending - new topics start in the Pending state; researchers must wait for the Steward to approve them //Approved - new topics start in the Approved state; researchers can use them immediately //TopicsIgnoredJustLog - all queries are logged and approved; researchers don't need to create topics emailDataSteward { // sendAuditEmails = true //false to turn off the whole works of emailing the data steward // interval = "1 day" //Audit researchers daily // timeAfterMidnight = "6 hours" //Audit researchers at 6 am. If the interval is less than 1 day then this delay is ignored. // maxQueryCountBetweenAudits = 30 //If a researcher runs more than this many queries since the last audit audit her // minTimeBetweenAudits = "30 days" //If a researcher runs at least one query, audit those queries if this much time has passed //You must provide the email address of the shrine node system admin, to handle bounces and invalid addresses //from = "shrine-admin@example.com" //You must provide the email address of the data steward //to = "shrine-steward@example.com" // subject = "Audit SHRINE researchers" //The baseUrl for the data steward to be substituted in to email text. Must be supplied if it is used in the email text. //stewardBaseUrl = "https://example.com:8443/steward/" //Text to use for the email audit. // AUDIT_LINES will be replaced by a researcherLine for each researcher to audit. // STEWARD_BASE_URL will be replaced by the value in stewardBaseUrl if available. // emailBody = """Please audit the following users at STEWARD_BASE_URL at your earliest convinience: // //AUDIT_LINES""" //note that this can be a multiline message //Text to use per researcher to audit. //FULLNAME, USERNAME, COUNT and LAST_AUDIT_DATE will be replaced with appropriate text. // researcherLine = "FULLNAME (USERNAME) has run COUNT queries since LAST_AUDIT_DATE." } // database { // dataSourceFrom = "JNDI" //Can be JNDI or testDataSource . Use testDataSource for tests, JNDI everywhere else // jndiDataSourceName = "java:comp/env/jdbc/stewardDB" //or leave out for tests // slickProfileClassName = "slick.driver.MySQLDriver$" // Can be // slick.driver.H2Driver$ // slick.driver.MySQLDriver$ // slick.driver.PostgresDriver$ // slick.driver.SQLServerDriver$ // slick.driver.JdbcDriver$ // freeslick.OracleProfile$ // freeslick.MSSQLServerProfile$ // // (Yes, with the $ on the end) // For testing without JNDI // testDataSource { //typical test settings for unit tests //driverClassName = "org.h2.Driver" //url = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1" //H2 embedded in-memory for unit tests //url = "jdbc:h2:~/stewardTest.h2" //H2 embedded on disk at ~/test // } // createTablesOnStart = false // true for testing with H2 in memory, when not running unit tests. Set to false normally // } // gruntWatch = false //false for production, true for mvn tomcat7:run . Allows the client javascript and html files to be loaded via gruntWatch . } email { //add javax mail properties from https://www.tutorialspoint.com/javamail_api/javamail_api_smtp_servers.htm here // javaxmail { // mail { // smtp { //for postfix on localhost // host = localhost // port = 25 //for AWS SES - See http://docs.aws.amazon.com/ses/latest/DeveloperGuide/send-using-smtp-java.html // host = email-smtp.us-east-1.amazonaws.com // port = 25 // transport.protocol = smtps // auth = true // starttls.enable = true // starttls.required = true // } // } // } //Must be set for AWS SES. See http://docs.aws.amazon.com/ses/latest/DeveloperGuide/send-using-smtp-java.html // authenticator { // username = yourUsername // password = yourPassword // } } } //Default settings for akka //akka { // loglevel = INFO // log-config-on-start = on // loggers = ["akka.event.slf4j.Slf4jLogger"] // Toggles whether the threads created by this ActorSystem should be daemons or not. Use daemonic inside of tomcat to support shutdown // daemonic = on //} //You'll see these settings for spray, baked into the .war files. //spray.servlet { // boot-class = "net.shrine.dashboard.net.shrine.metadata.Boot" //Don't change this one. It'll start the wrong (or no) application if you change it. // request-timeout = 30s //}