diff --git a/RELEASING.md b/RELEASING.md index 40c495d28d..deadc7901c 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -72,6 +72,8 @@ $ VERSION_FILES=( examples/android/clientcache/app/build.gradle examples/android/helloworld/app/build.gradle examples/android/routeguide/app/build.gradle + examples/example-kotlin/build.gradle + examples/example-kotlin/android/helloworld/app/build.gradle ) ``` diff --git a/buildscripts/kokoro/android.sh b/buildscripts/kokoro/android.sh index a52ab10400..2ef24aba11 100755 --- a/buildscripts/kokoro/android.sh +++ b/buildscripts/kokoro/android.sh @@ -44,6 +44,8 @@ cd ../routeguide cd ../helloworld ./gradlew build +cd $BASE_DIR/github/grpc-java/examples/example-kotlin/android/helloworld/ +./gradlew build # Skip APK size and dex count comparisons for non-PR builds diff --git a/buildscripts/kokoro/unix.sh b/buildscripts/kokoro/unix.sh index afcb7793e3..b68327aee1 100755 --- a/buildscripts/kokoro/unix.sh +++ b/buildscripts/kokoro/unix.sh @@ -56,11 +56,18 @@ fi # Run tests ./gradlew build $GRADLE_FLAGS +./gradlew install + pushd examples ./gradlew build $GRADLE_FLAGS # --batch-mode reduces log spam mvn verify --batch-mode popd + +pushd examples/example-kotlin/ +./gradlew build $GRADLE_FLAGS +popd + # TODO(zpencer): also build the GAE examples LOCAL_MVN_TEMP=$(mktemp -d) diff --git a/examples/example-kotlin/README.md b/examples/example-kotlin/README.md new file mode 100644 index 0000000000..aec61f7dd4 --- /dev/null +++ b/examples/example-kotlin/README.md @@ -0,0 +1,59 @@ +grpc Kotlin example +============================================== + +The examples require grpc-java to already be built. You are strongly encouraged +to check out a git release tag, since there will already be a build of grpc +available. Otherwise you must follow COMPILING.md. + +You may want to read through the +[Quick Start Guide](https://grpc.io/docs/quickstart/java.html) +before trying out the examples. + +To build the examples, run in this directory: + +``` +$ ./gradlew installDist +``` + +This creates the scripts `hello-world-server`, `hello-world-client`, +`route-guide-server`, and `route-guide-client` in the +`build/install/examples/bin/` directory that run the examples. Each +example requires the server to be running before starting the client. + +For example, to try the hello world example first run: + +``` +$ ./build/install/examples/bin/hello-world-server +``` + +And in a different terminal window run: + +``` +$ ./build/install/examples/bin/hello-world-client +``` + +That's it! + +Please refer to gRPC Java's [README](../README.md) and +[tutorial](https://grpc.io/docs/tutorials/basic/java.html) for more +information. + +Unit test examples +============================================== + +Examples for unit testing gRPC clients and servers are located in [./src/test](./src/test). + +In general, we DO NOT allow overriding the client stub. +We encourage users to leverage `InProcessTransport` as demonstrated in the examples to +write unit tests. `InProcessTransport` is light-weight and runs the server +and client in the same process without any socket/TCP connection. + +For testing a gRPC client, create the client with a real stub +using an InProcessChannelBuilder.java and test it against an InProcessServer.java +with a mock/fake service implementation. + +For testing a gRPC server, create the server as an InProcessServer, +and test it against a real client stub with an InProcessChannel. + +The gRPC-java library also provides a JUnit rule, GrpcServerRule.java, to do the starting +up and shutting down boilerplate for you. diff --git a/examples/example-kotlin/android/helloworld/app/build.gradle b/examples/example-kotlin/android/helloworld/app/build.gradle new file mode 100644 index 0000000000..ba05ae6214 --- /dev/null +++ b/examples/example-kotlin/android/helloworld/app/build.gradle @@ -0,0 +1,91 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' +apply plugin: 'com.google.protobuf' + +android { + compileSdkVersion 27 + + defaultConfig { + applicationId "io.grpc.helloworldexample" + // API level 14+ is required for TLS since Google Play Services v10.2 + minSdkVersion 14 + targetSdkVersion 27 + versionCode 1 + versionName "1.0" + } + buildTypes { + debug { + minifyEnabled false + } + release { + minifyEnabled true + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + lintOptions { + disable 'GoogleAppIndexingWarning', 'HardcodedText', 'InvalidPackage' + textReport true + textOutput "stdout" + } + // Android Studio 3.1 does not automatically pick up '/kotlin' as source input + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + main.java.srcDirs += 'src/test/kotlin' + androidTest.java.srcDirs += 'src/androidTest/kotlin' + } + + lintOptions { + // Do not complain about outdated deps, so that this can javax.annotation-api can be same + // as other projects in this repo. Your project is not required to do this, and can + // upgrade the dep. + disable 'GradleDependency' + // The Android linter does not correctly detect resources used in Kotlin. + // See: + // - https://youtrack.jetbrains.com/issue/KT-7729 + // - https://youtrack.jetbrains.com/issue/KT-12499 + disable 'UnusedResources' + textReport true + textOutput "stdout" + } +} + +protobuf { + protoc { + artifact = 'com.google.protobuf:protoc:3.5.1-1' + } + plugins { + javalite { + artifact = "com.google.protobuf:protoc-gen-javalite:3.0.0" + } + grpc { + artifact = 'io.grpc:protoc-gen-grpc-java:1.13.0-SNAPSHOT' // CURRENT_GRPC_VERSION + } + } + generateProtoTasks { + all().each { task -> + task.plugins { + javalite {} + grpc { + // Options added to --grpc_out + option 'lite' + } + } + } + } +} + +dependencies { + compile 'com.android.support:appcompat-v7:27.0.2' + compile 'javax.annotation:javax.annotation-api:1.2' + compile "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + + // You need to build grpc-java to obtain these libraries below. + compile 'io.grpc:grpc-okhttp:1.13.0-SNAPSHOT' // CURRENT_GRPC_VERSION + compile 'io.grpc:grpc-protobuf-lite:1.13.0-SNAPSHOT' // CURRENT_GRPC_VERSION + compile 'io.grpc:grpc-stub:1.13.0-SNAPSHOT' // CURRENT_GRPC_VERSION +} + +repositories { + mavenCentral() +} diff --git a/examples/example-kotlin/android/helloworld/app/proguard-rules.pro b/examples/example-kotlin/android/helloworld/app/proguard-rules.pro new file mode 100644 index 0000000000..1507a52678 --- /dev/null +++ b/examples/example-kotlin/android/helloworld/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in $ANDROID_HOME/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +-dontwarn com.google.common.** +# Ignores: can't find referenced class javax.lang.model.element.Modifier +-dontwarn com.google.errorprone.annotations.** +-dontwarn javax.naming.** +-dontwarn okio.** +-dontwarn sun.misc.Unsafe diff --git a/examples/example-kotlin/android/helloworld/app/src/main/AndroidManifest.xml b/examples/example-kotlin/android/helloworld/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..eee4057cd0 --- /dev/null +++ b/examples/example-kotlin/android/helloworld/app/src/main/AndroidManifest.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + diff --git a/examples/example-kotlin/android/helloworld/app/src/main/kotlin/io/grpc/helloworldexample/HelloworldActivity.kt b/examples/example-kotlin/android/helloworld/app/src/main/kotlin/io/grpc/helloworldexample/HelloworldActivity.kt new file mode 100644 index 0000000000..bcc6d0f28d --- /dev/null +++ b/examples/example-kotlin/android/helloworld/app/src/main/kotlin/io/grpc/helloworldexample/HelloworldActivity.kt @@ -0,0 +1,100 @@ +/* + * Copyright 2015, gRPC Authors All rights reserved. + * + * Licensed 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 io.grpc.helloworldexample + +import android.app.Activity +import android.content.Context +import android.os.AsyncTask +import android.os.Bundle +import android.support.v7.app.AppCompatActivity +import android.text.TextUtils +import android.text.method.ScrollingMovementMethod +import android.view.View +import android.view.inputmethod.InputMethodManager +import android.widget.Button +import android.widget.TextView +import io.grpc.ManagedChannel +import io.grpc.ManagedChannelBuilder +import io.grpc.examples.helloworld.GreeterGrpc +import io.grpc.examples.helloworld.HelloRequest +import java.io.PrintWriter +import java.io.StringWriter +import java.lang.ref.WeakReference +import java.util.concurrent.TimeUnit +import kotlinx.android.synthetic.main.activity_helloworld.* + +class HelloworldActivity : AppCompatActivity(), View.OnClickListener { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_helloworld) + grpc_response_text!!.movementMethod = ScrollingMovementMethod() + send_button!!.setOnClickListener(this) + } + + override fun onClick(view: View) { + (getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager) + .hideSoftInputFromWindow(host_edit_text!!.windowToken, 0) + send_button!!.isEnabled = false + grpc_response_text!!.text = "" + GrpcTask(this) + .execute( + host_edit_text!!.text.toString(), + message_edit_text!!.text.toString(), + port_edit_text!!.text.toString()) + } + + private class GrpcTask constructor(activity: Activity) : AsyncTask() { + private val activityReference: WeakReference = WeakReference(activity) + private var channel: ManagedChannel? = null + + override fun doInBackground(vararg params: String): String { + val host = params[0] + val message = params[1] + val portStr = params[2] + val port = if (TextUtils.isEmpty(portStr)) 0 else Integer.valueOf(portStr) + return try { + channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build() + val stub = GreeterGrpc.newBlockingStub(channel) + val request = HelloRequest.newBuilder().setName(message).build() + val reply = stub.sayHello(request) + reply.message + } catch (e: Exception) { + val sw = StringWriter() + val pw = PrintWriter(sw) + e.printStackTrace(pw) + pw.flush() + + "Failed... : %s".format(sw) + } + } + + override fun onPostExecute(result: String) { + try { + channel?.shutdown()?.awaitTermination(1, TimeUnit.SECONDS) + } catch (e: InterruptedException) { + Thread.currentThread().interrupt() + } + + val activity = activityReference.get() ?: return + val resultText: TextView = activity.findViewById(R.id.grpc_response_text) + val sendButton: Button = activity.findViewById(R.id.send_button) + + resultText.text = result + sendButton.isEnabled = true + } + } +} diff --git a/examples/example-kotlin/android/helloworld/app/src/main/proto/helloworld.proto b/examples/example-kotlin/android/helloworld/app/src/main/proto/helloworld.proto new file mode 100644 index 0000000000..78441b2d53 --- /dev/null +++ b/examples/example-kotlin/android/helloworld/app/src/main/proto/helloworld.proto @@ -0,0 +1,38 @@ +// Copyright 2015, gRPC Authors +// All rights reserved. +// +// Licensed 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. +syntax = "proto3"; + +option java_multiple_files = true; +option java_package = "io.grpc.examples.helloworld"; +option java_outer_classname = "HelloWorldProto"; +option objc_class_prefix = "HLW"; + +package helloworld; + +// The greeting service definition. +service Greeter { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) {} +} + +// The request message containing the user's name. +message HelloRequest { + string name = 1; +} + +// The response message containing the greetings +message HelloReply { + string message = 1; +} diff --git a/examples/example-kotlin/android/helloworld/app/src/main/res/layout/activity_helloworld.xml b/examples/example-kotlin/android/helloworld/app/src/main/res/layout/activity_helloworld.xml new file mode 100644 index 0000000000..e9f41f4669 --- /dev/null +++ b/examples/example-kotlin/android/helloworld/app/src/main/res/layout/activity_helloworld.xml @@ -0,0 +1,54 @@ + + + + + + + + + + +