diff --git a/.gitignore b/.gitignore index 3e3d6a37d..71be914a0 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,4 @@ ldap kyuubi-server/kyuubi-kdc/ metrics/report.json metrics/.report.json.crc +/kyuubi-ha/embedded_zookeeper/ diff --git a/kyuubi-assembly/pom.xml b/kyuubi-assembly/pom.xml index 6a128c112..cc84848a2 100644 --- a/kyuubi-assembly/pom.xml +++ b/kyuubi-assembly/pom.xml @@ -24,7 +24,7 @@ kyuubi org.apache.kyuubi - 0.8.0-SNAPSHOT + 1.0.0-SNAPSHOT ../pom.xml @@ -36,7 +36,7 @@ org.apache.kyuubi kyuubi-common - 0.8.0-SNAPSHOT + ${project.version} diff --git a/kyuubi-common/pom.xml b/kyuubi-common/pom.xml index 74b08dc5b..5f52ce64a 100644 --- a/kyuubi-common/pom.xml +++ b/kyuubi-common/pom.xml @@ -22,7 +22,7 @@ org.apache.kyuubi kyuubi - 0.8.0-SNAPSHOT + 1.0.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/Utils.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/Utils.scala index 0580a4130..9c8999126 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/Utils.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/Utils.scala @@ -18,10 +18,12 @@ package org.apache.kyuubi import java.io.{File, InputStreamReader, IOException} +import java.net.{URI, URISyntaxException} import java.nio.charset.StandardCharsets -import java.util.Properties +import java.util.{Properties, UUID} import scala.collection.JavaConverters._ +import scala.util.{Success, Try} private[kyuubi] object Utils extends Logging { @@ -61,4 +63,61 @@ private[kyuubi] object Utils extends Logging { } }.getOrElse(Map.empty) } + + + /** + * Return a well-formed URI for the file described by a user input string. + * + * If the supplied path does not contain a scheme, or is a relative path, it will be + * converted into an absolute path with a file:// scheme. + */ + def resolveURI(path: String): URI = { + try { + val uri = new URI(path) + if (uri.getScheme != null) { + return uri + } + // make sure to handle if the path has a fragment (applies to yarn + // distributed cache) + if (uri.getFragment != null) { + val absoluteURI = new File(uri.getPath).getAbsoluteFile.toURI + return new URI(absoluteURI.getScheme, absoluteURI.getHost, absoluteURI.getPath, + uri.getFragment) + } + } catch { + case _: URISyntaxException => + } + new File(path).getAbsoluteFile.toURI + } + + private val MAX_DIR_CREATION_ATTEMPTS: Int = 10 + + /** + * Create a directory inside the given parent directory. The directory is guaranteed to be + * newly created, and is not marked for automatic deletion. + */ + def createDirectory(root: String, namePrefix: String = "kyuubi"): File = { + (0 until MAX_DIR_CREATION_ATTEMPTS).foreach { _ => + val dir = new File(root, namePrefix + "-" + UUID.randomUUID.toString) + Try { dir.mkdirs() } match { + case Success(_) => return dir + case _ => + } + } + throw new IOException("Failed to create a temp directory (under " + root + ") after " + + MAX_DIR_CREATION_ATTEMPTS + " attempts!") + } + + /** + * Create a temporary directory inside the given parent directory. The directory will be + * automatically deleted when the VM shuts down. + */ + def createTempDir( + root: String = System.getProperty("java.io.tmpdir"), + namePrefix: String = "kyuubi"): File = { + val dir = createDirectory(root, namePrefix) + dir.deleteOnExit() + dir + } + } diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala index c5493be27..a7a9ce152 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala @@ -23,6 +23,7 @@ import org.apache.kyuubi.Utils case class KyuubiConf(loadSysDefault: Boolean = true) { private val settings = new ConcurrentHashMap[String, String]() + private lazy val reader: ConfigProvider = new ConfigProvider(settings) if (loadSysDefault) { loadSysProps() @@ -35,6 +36,11 @@ case class KyuubiConf(loadSysDefault: Boolean = true) { this } + def set[T](entry: ConfigEntry[T], value: T): KyuubiConf = { + settings.put(entry.key, entry.strConverter(value)) + this + } + def set(key: String, value: String): KyuubiConf = { require(key != null) require(value != null) @@ -42,6 +48,10 @@ case class KyuubiConf(loadSysDefault: Boolean = true) { this } + def get[T](config: ConfigEntry[T]): T = { + config.readFrom(reader) + } + } object KyuubiConf { @@ -51,4 +61,27 @@ object KyuubiConf { /** the default file that contains kyuubi properties */ final val KYUUBI_CONF_FILE_NAME = "kyuubi-defaults.conf" final val KYUUBI_HOME = "KYUUBI_HOME" + + def buildConf(key: String): ConfigBuilder = ConfigBuilder(KYUUBI_PREFIX + key) + + val EMBEDDED_ZK_PORT: ConfigEntry[Int] = + buildConf("embedded.zk.port") + .doc("The port of the embedded zookeeper server") + .version("1.0.0") + .intConf.checkValue(_ >= 0, s"The value of $EMBEDDED_ZK_PORT must be >= 0") + .createWithDefault(2181) + + val EMBEDDED_ZK_TEMP_DIR: ConfigEntry[String] = + buildConf("embedded.zk.directory") + .doc("The temporary directory for the embedded zookeeper server") + .version("1.0.0") + .stringConf + .createWithDefault(Utils.resolveURI("embedded_zookeeper").getRawPath) + + val HA_ZK_QUORUM: OptionalConfigEntry[Seq[String]] = + buildConf("ha.zk.quorum") + .version("1.0.0") + .stringConf + .toSequence + .createOptional } diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/AbstractService.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/AbstractService.scala index fbb02b387..d07f9fcf3 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/AbstractService.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/AbstractService.scala @@ -22,20 +22,20 @@ import org.apache.kyuubi.config.KyuubiConf abstract class AbstractService(serviceName: String) extends Service with Logging { import ServiceState._ - private var conf: KyuubiConf = _ - private var state: ServiceState = NEW - private var startTime: Long = _ + protected var conf: KyuubiConf = _ + protected var state: ServiceState = LATENT + protected var startTime: Long = _ /** * Initialize the service. * - * The transition must be from [[NEW]]to [[INITIALIZED]] unless the + * The transition must be from [[LATENT]]to [[INITIALIZED]] unless the * operation failed and an exception was raised. * * @param conf the configuration of the service */ override def initialize(conf: KyuubiConf): Unit = { - ensureCurrentState(NEW) + ensureCurrentState(LATENT) this.conf = conf changeState(INITIALIZED) info(s"Service[$serviceName] is initialized.") @@ -62,7 +62,7 @@ abstract class AbstractService(serviceName: String) extends Service with Logging */ override def stop(): Unit = { state match { - case NEW | INITIALIZED | STOPPED => + case LATENT | INITIALIZED | STOPPED => case _ => ensureCurrentState(STARTED) changeState(STOPPED) diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/Service.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/Service.scala index dba38a339..d0c31066e 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/Service.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/Service.scala @@ -24,7 +24,7 @@ trait Service { /** * Initialize the service. * - * The transition must be from [[NEW]]to [[INITIALIZED]] unless the + * The transition must be from [[LATENT]]to [[INITIALIZED]] unless the * operation failed and an exception was raised. * * @param conf the configuration of the service diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/ServiceState.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/ServiceState.scala index 120b4d940..50235d8ad 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/ServiceState.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/ServiceState.scala @@ -24,7 +24,7 @@ private[kyuubi] object ServiceState extends Enumeration { type ServiceState = Value val - /** Constructed but not initialized */ NEW, + /** Constructed but not initialized */ LATENT, /** Initialized but not started or stopped */ INITIALIZED, /** Started and not stopped */ STARTED, /** Stopped. No further state transitions are permitted */ STOPPED = Value diff --git a/kyuubi-common/src/test/resources/log4j.properties b/kyuubi-common/src/test/resources/log4j.properties new file mode 100644 index 000000000..c0f79896c --- /dev/null +++ b/kyuubi-common/src/test/resources/log4j.properties @@ -0,0 +1,36 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Set everything to be logged to the file target/unit-tests.log +log4j.rootLogger=DEBUG, CA, FA + +#Console Appender +log4j.appender.CA=org.apache.log4j.ConsoleAppender +log4j.appender.CA.layout=org.apache.log4j.PatternLayout +log4j.appender.CA.layout.ConversionPattern=%d{HH:mm:ss.SSS} %p %c: %m%n +log4j.appender.CA.Threshold = WARN + + +#File Appender +log4j.appender.FA=org.apache.log4j.FileAppender +log4j.appender.FA.append=false +log4j.appender.FA.file=target/unit-tests.log +log4j.appender.FA.layout=org.apache.log4j.PatternLayout +log4j.appender.FA.layout.ConversionPattern=%d{HH:mm:ss.SSS} %t %p %c{2}: %m%n + +# Set the logger level of File Appender to WARN +log4j.appender.FA.Threshold = DEBUG \ No newline at end of file diff --git a/kyuubi-common/src/test/scala/org/apache/kyuubi/UtilsSuite.scala b/kyuubi-common/src/test/scala/org/apache/kyuubi/UtilsSuite.scala index f12e2ae66..4a4cb1cd5 100644 --- a/kyuubi-common/src/test/scala/org/apache/kyuubi/UtilsSuite.scala +++ b/kyuubi-common/src/test/scala/org/apache/kyuubi/UtilsSuite.scala @@ -49,4 +49,25 @@ class UtilsSuite extends KyuubiFunSuite { assert(props("kyuubi.yes") === "yes") assert(!props.contains("kyuubi.no")) } + + test("resolveURI") { + def assertResolves(before: String, after: String): Unit = { + // This should test only single paths + assert(before.split(",").length === 1) + def resolve(uri: String): String = Utils.resolveURI(uri).toString + assert(resolve(before) === after) + assert(resolve(after) === after) + // Repeated invocations of resolveURI should yield the same result + assert(resolve(resolve(after)) === after) + assert(resolve(resolve(resolve(after))) === after) + } + assertResolves("hdfs:/root/spark.jar", "hdfs:/root/spark.jar") + assertResolves("hdfs:///root/spark.jar#app.jar", "hdfs:///root/spark.jar#app.jar") + assertResolves("file:/C:/path/to/file.txt", "file:/C:/path/to/file.txt") + assertResolves("file:///C:/path/to/file.txt", "file:///C:/path/to/file.txt") + assertResolves("file:/C:/file.txt#alias.txt", "file:/C:/file.txt#alias.txt") + assertResolves("file:foo", "file:foo") + assertResolves("file:foo:baby", "file:foo:baby") + } + } diff --git a/kyuubi-common/src/test/scala/org/apache/kyuubi/config/KyuubiConfSuite.scala b/kyuubi-common/src/test/scala/org/apache/kyuubi/config/KyuubiConfSuite.scala new file mode 100644 index 000000000..4e42c8f3e --- /dev/null +++ b/kyuubi-common/src/test/scala/org/apache/kyuubi/config/KyuubiConfSuite.scala @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.kyuubi.config + +import org.apache.kyuubi.KyuubiFunSuite + +class KyuubiConfSuite extends KyuubiFunSuite { + + import KyuubiConf._ + + test("kyuubi conf defaults") { + val conf = new KyuubiConf() + assert(conf.get(EMBEDDED_ZK_PORT) === 2181) + assert(conf.get(EMBEDDED_ZK_TEMP_DIR).endsWith("embedded_zookeeper")) + assert(conf.get(HA_ZK_QUORUM) === None) + } +} diff --git a/kyuubi-ha/pom.xml b/kyuubi-ha/pom.xml new file mode 100644 index 000000000..c35e14690 --- /dev/null +++ b/kyuubi-ha/pom.xml @@ -0,0 +1,59 @@ + + + + + + org.apache.kyuubi + kyuubi + 1.0.0-SNAPSHOT + ../pom.xml + + 4.0.0 + + kyuubi-ha + jar + Kyuubi Project High Availability + + + + org.apache.kyuubi + kyuubi-common + ${project.version} + + + + org.apache.curator + curator-test + + + + org.apache.curator + curator-framework + + + org.apache.kyuubi + kyuubi-common + ${project.version} + test + test-jar + + + + diff --git a/kyuubi-ha/src/main/scala/org/apache/kyuubi/ha/server/EmbeddedZkServer.scala b/kyuubi-ha/src/main/scala/org/apache/kyuubi/ha/server/EmbeddedZkServer.scala new file mode 100644 index 000000000..325268f97 --- /dev/null +++ b/kyuubi-ha/src/main/scala/org/apache/kyuubi/ha/server/EmbeddedZkServer.scala @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.kyuubi.ha.server + +import java.io.File + +import org.apache.curator.test.TestingServer + +import org.apache.kyuubi.config.KyuubiConf +import org.apache.kyuubi.service.AbstractService + +/** + * An embedded zookeeper server for testing and quick try with Kyuubi with external + * zookeeper cluster. + * + * @note Avoid to use this for production purpose + * + * @param name the service name + */ +class EmbeddedZkServer private(name: String) extends AbstractService(name) { + + def this() = this(classOf[EmbeddedZkServer].getSimpleName) + + private var server: TestingServer = _ + + override def initialize(conf: KyuubiConf): Unit = { + this.conf = conf + val port = conf.get(KyuubiConf.EMBEDDED_ZK_PORT) + val dataDir = conf.get(KyuubiConf.EMBEDDED_ZK_TEMP_DIR) + server = new TestingServer(port, new File(dataDir), false) + super.initialize(conf) + } + + override def start(): Unit = { + server.start() + super.start() + } + + override def stop(): Unit = { + if (server != null) { + server.close() + } + server = null + super.stop() + } + + def getConnectString: String = { + if (server == null) { + null + } else { + server.getConnectString + } + } +} diff --git a/kyuubi-ha/src/test/resources/log4j.properties b/kyuubi-ha/src/test/resources/log4j.properties new file mode 100644 index 000000000..c0f79896c --- /dev/null +++ b/kyuubi-ha/src/test/resources/log4j.properties @@ -0,0 +1,36 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Set everything to be logged to the file target/unit-tests.log +log4j.rootLogger=DEBUG, CA, FA + +#Console Appender +log4j.appender.CA=org.apache.log4j.ConsoleAppender +log4j.appender.CA.layout=org.apache.log4j.PatternLayout +log4j.appender.CA.layout.ConversionPattern=%d{HH:mm:ss.SSS} %p %c: %m%n +log4j.appender.CA.Threshold = WARN + + +#File Appender +log4j.appender.FA=org.apache.log4j.FileAppender +log4j.appender.FA.append=false +log4j.appender.FA.file=target/unit-tests.log +log4j.appender.FA.layout=org.apache.log4j.PatternLayout +log4j.appender.FA.layout.ConversionPattern=%d{HH:mm:ss.SSS} %t %p %c{2}: %m%n + +# Set the logger level of File Appender to WARN +log4j.appender.FA.Threshold = DEBUG \ No newline at end of file diff --git a/kyuubi-ha/src/test/scala/org/apache/kyuubi/ha/server/EmbeddedZkServerSuite.scala b/kyuubi-ha/src/test/scala/org/apache/kyuubi/ha/server/EmbeddedZkServerSuite.scala new file mode 100644 index 000000000..1fa67bb71 --- /dev/null +++ b/kyuubi-ha/src/test/scala/org/apache/kyuubi/ha/server/EmbeddedZkServerSuite.scala @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.kyuubi.ha.server + +import org.apache.curator.framework.CuratorFrameworkFactory +import org.apache.curator.framework.imps.CuratorFrameworkState +import org.apache.curator.retry.ExponentialBackoffRetry + +import org.apache.kyuubi.KyuubiFunSuite +import org.apache.kyuubi.config.KyuubiConf +import org.apache.kyuubi.service.ServiceState._ + +class EmbeddedZkServerSuite extends KyuubiFunSuite { + + test("embedded zookeeper server") { + val zkServer = new EmbeddedZkServer() + assert(zkServer.getConf == null) + assert(zkServer.getName === zkServer.getClass.getSimpleName) + assert(zkServer.getServiceState === LATENT) + val conf = KyuubiConf() + zkServer.initialize(conf) + assert(zkServer.getConf === conf) + assert(zkServer.getServiceState === INITIALIZED) + assert(zkServer.getConnectString.endsWith("2181")) + assert(zkServer.getStartTime === 0) + zkServer.start() + assert(zkServer.getServiceState === STARTED) + assert(zkServer.getConnectString.endsWith("2181")) + assert(zkServer.getStartTime !== 0) + zkServer.stop() + assert(zkServer.getServiceState === STOPPED) + } + + test("connect test with embedded zookeeper") { + val zkServer = new EmbeddedZkServer() + zkServer.initialize(KyuubiConf()) + zkServer.start() + + val zkClient = CuratorFrameworkFactory.builder() + .connectString(zkServer.getConnectString) + .sessionTimeoutMs(5000) + .retryPolicy(new ExponentialBackoffRetry(1000, 3)) + .build + zkClient.start() + + assert(zkClient.getState === CuratorFrameworkState.STARTED) + assert(zkClient.getZookeeperClient.blockUntilConnectedOrTimedOut()) + } +} diff --git a/kyuubi-hive-thrift/pom.xml b/kyuubi-hive-thrift/pom.xml index a548c13ac..25331fb1b 100644 --- a/kyuubi-hive-thrift/pom.xml +++ b/kyuubi-hive-thrift/pom.xml @@ -22,11 +22,10 @@ kyuubi org.apache.kyuubi - 0.8.0-SNAPSHOT + 1.0.0-SNAPSHOT 4.0.0 - org.apache.kyuubi kyuubi-hive-thrift Kyuubi Project Hive Thrift IDL jar diff --git a/kyuubi-server/pom.xml b/kyuubi-server/pom.xml index 07b1d69d4..fd15bda73 100644 --- a/kyuubi-server/pom.xml +++ b/kyuubi-server/pom.xml @@ -22,7 +22,7 @@ kyuubi org.apache.kyuubi - 0.8.0-SNAPSHOT + 1.0.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/kyuubi-spark-sql-engine/pom.xml b/kyuubi-spark-sql-engine/pom.xml index 133e72910..7e7731136 100644 --- a/kyuubi-spark-sql-engine/pom.xml +++ b/kyuubi-spark-sql-engine/pom.xml @@ -22,7 +22,7 @@ kyuubi org.apache.kyuubi - 0.8.0-SNAPSHOT + 1.0.0-SNAPSHOT 4.0.0 diff --git a/kyuubi-thrift/pom.xml b/kyuubi-thrift/pom.xml index 65bee8e88..a0a5331dc 100644 --- a/kyuubi-thrift/pom.xml +++ b/kyuubi-thrift/pom.xml @@ -22,7 +22,7 @@ kyuubi org.apache.kyuubi - 0.8.0-SNAPSHOT + 1.0.0-SNAPSHOT 4.0.0 diff --git a/pom.xml b/pom.xml index d5fa4e77e..02326a716 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.apache.kyuubi kyuubi Kyuubi Project Parent - 0.8.0-SNAPSHOT + 1.0.0-SNAPSHOT kyuubi-common kyuubi-thrift @@ -31,6 +31,7 @@ kyuubi-assembly kyuubi-hive-thrift kyuubi-spark-sql-engine + kyuubi-ha pom @@ -70,7 +71,7 @@ 1.1 ${project.build.directory}/scala-${scala.binary.version}/jars 2.0.0-M15 - 2.6.0 + 2.7.1 3.1.2 2.6 0.12.0 @@ -250,6 +251,13 @@ provided + + org.apache.curator + curator-test + ${curator.version} + provided + + org.apache.curator curator-recipes @@ -407,7 +415,7 @@ org.apache.maven.plugins maven-compiler-plugin - ${maven.version} + ${maven.version} ${java.version} ${java.version} @@ -489,11 +497,15 @@ org.scalatest scalatest-maven-plugin - 1.0 + 2.0.0 ${project.build.directory}/surefire-reports . TestSuite.txt + + src/test/resources/log4j.properties + ${project.build.directory}/tmp + @@ -637,6 +649,11 @@ + + net.alchim31.maven + scala-maven-plugin + + org.scalastyle scalastyle-maven-plugin