1. مقدمة
أضاف الإصدار 5.0 من Spring Framework إمكانية استخدام Kotlin، ما يسهّل على مطوّري Kotlin استخدام Spring. نتيجةً لذلك، أدت هذه التغييرات إلى أنّ عمليات الدمج في Google Cloud التي توفّرها Spring Cloud GCP تعمل أيضًا بسلاسة في Kotlin. في هذا الدرس العملي، ستتعرّف على مدى سهولة بدء استخدام خدمات Google Cloud في تطبيقات Kotlin.
يشرح هذا الدرس التطبيقي حول الترميز كيفية إعداد تطبيق تسجيل بسيط بلغة Kotlin يعرض كيفية استخدام خدمات Google Cloud Platform، بما في ذلك Cloud Pub/Sub وCloud SQL.
ما ستنشئه
في هذا الدرس التطبيقي حول الترميز، ستعدّ تطبيق Kotlin Spring Boot يقبل معلومات المسجّلين وينشرها في موضوع Cloud Pub/Sub ويحفظها في قاعدة بيانات Cloud MySQL.
أهداف الدورة التعليمية
كيفية الدمج مع خدمات Google Cloud في تطبيق Kotlin Spring
المتطلبات
- مشروع على Google Cloud Platform
- متصفّح، مثل Chrome أو Firefox
كيف ستستخدم هذا البرنامج التعليمي؟
ما هو تقييمك لتجربة إنشاء تطبيقات ويب باستخدام HTML/CSS؟
ما هو تقييمك لتجربة استخدام خدمات Google Cloud Platform؟
2. الإعداد والمتطلبات
إعداد البيئة بالسرعة التي تناسبك
- سجِّل الدخول إلى Cloud Console وأنشِئ مشروعًا جديدًا أو أعِد استخدام مشروع حالي. (إذا لم يكن لديك حساب على Gmail أو G Suite، عليك إنشاء حساب).
تذكَّر رقم تعريف المشروع، وهو اسم فريد في جميع مشاريع Google Cloud (الاسم أعلاه مستخدَم حاليًا ولن يكون متاحًا لك، نأسف لذلك). سيتم الإشارة إليه لاحقًا في هذا الدرس العملي باسم PROJECT_ID.
- بعد ذلك، عليك تفعيل الفوترة في Cloud Console من أجل استخدام موارد Google Cloud.
لن تكلفك تجربة هذا الدرس التطبيقي حول الترميز الكثير من المال، إن لم تكلفك شيئًا على الإطلاق. احرص على اتّباع أي تعليمات في قسم "التنظيف" الذي ينصحك بكيفية إيقاف الموارد حتى لا تتحمّل رسومًا تتجاوز هذا البرنامج التعليمي. يمكن لمستخدمي Google Cloud الجدد الاستفادة من برنامج الفترة التجريبية المجانية بقيمة 300 دولار أمريكي.
Google Cloud Shell
على الرغم من إمكانية تشغيل Google Cloud عن بُعد من الكمبيوتر المحمول، سنستخدم في هذا الدرس التطبيقي حول الترميز Google Cloud Shell، وهي بيئة سطر أوامر تعمل في السحابة الإلكترونية.
تفعيل Cloud Shell
- من Cloud Console، انقر على تفعيل Cloud Shell
.
إذا لم يسبق لك بدء Cloud Shell، ستظهر لك شاشة وسيطة (الجزء السفلي غير المرئي من الصفحة) توضّح ماهيته. في هذه الحالة، انقر على متابعة (ولن تظهر لك مرة أخرى). في ما يلي الشكل الذي ستظهر به هذه الشاشة لمرة واحدة:
يستغرق توفير Cloud Shell والاتصال به بضع لحظات فقط.
يتم تحميل هذه الآلة الافتراضية مزوّدة بكل أدوات التطوير التي ستحتاج إليها. توفّر هذه الخدمة دليلًا رئيسيًا دائمًا بسعة 5 غيغابايت وتعمل في Google Cloud، ما يؤدي إلى تحسين أداء الشبكة والمصادقة بشكل كبير. يمكن إنجاز معظم العمل في هذا الدرس التطبيقي حول الترميز، إن لم يكن كله، باستخدام متصفّح أو جهاز Chromebook فقط.
بعد الاتصال بـ Cloud Shell، من المفترض أن يظهر لك أنّه تم إثبات هويتك وأنّه تم ضبط المشروع على رقم تعريف مشروعك.
- نفِّذ الأمر التالي في Cloud Shell للتأكّد من إكمال عملية المصادقة:
gcloud auth list
ناتج الأمر
Credentialed Accounts
ACTIVE ACCOUNT
* <my_account>@<my_domain.com>
To set the active account, run:
$ gcloud config set account `ACCOUNT`
gcloud config list project
ناتج الأمر
[core] project = <PROJECT_ID>
إذا لم يكن كذلك، يمكنك تعيينه من خلال هذا الأمر:
gcloud config set project <PROJECT_ID>
ناتج الأمر
Updated property [core/project].
3- توفير موارد Pub/Sub
أولاً، علينا إعداد موضوع واشتراك في Cloud Pub/Sub. في هذا التطبيق، سننشر معلومات التسجيل في موضوع Pub/Sub، ثم تتم قراءة المعلومات من هذا الموضوع وحفظها في قاعدة بيانات.
في هذا البرنامج التعليمي، سنعتمد على Cloud Shell لتوفير مواردنا. يُرجى العِلم أنّه يمكن أيضًا ضبط موارد Pub/Sub من خلال قسم Cloud Pub/Sub في Google Cloud Console.
في نافذة Cloud Shell، فعِّل واجهة Pub/Sub API أولاً.
$ gcloud services enable pubsub.googleapis.com
بعد ذلك، سننشئ موضوعًا على Pub/Sub باسم registrations لهذا التطبيق. سيتم نشر معلومات التسجيل التي تم إرسالها من خلال التطبيق في هذا الموضوع.
$ gcloud pubsub topics create registrations
أخيرًا، أنشئ اشتراكًا في الموضوع. يتيح لك اشتراك Pub/Sub تلقّي الرسائل من موضوع معيّن.
$ gcloud pubsub subscriptions create registrations-sub --topic=registrations
لقد انتهيت الآن من إنشاء موضوع واشتراك في Cloud Pub/Sub لتطبيقك.
4. إنشاء مثيل وقاعدة بيانات Cloud SQL (MySQL)
بالنسبة إلى التطبيق النموذجي، نحتاج أيضًا إلى إعداد مثيل قاعدة بيانات لتخزين معلومات المسجِّل. ستعتمد هذه الخطوة أيضًا على وحدة Cloud Shell الطرفية لتوفير موارد Cloud SQL. يُرجى العِلم أنّه يمكنك أيضًا عرض آلاتك الافتراضية في Cloud SQL وضبطها من خلال Google Cloud Console.
أولاً، فعِّل واجهة برمجة التطبيقات Admin API في Cloud SQL.
$ gcloud services enable sqladmin.googleapis.com
بعد ذلك، سنوفّر مثيلاً من Cloud SQL (MySQL). قد يستغرق تنفيذ هذا الأمر بعض الوقت.
$ gcloud sql instances create codelab-instance --region=us-east1
بعد إنشاء مثيل Cloud SQL بنجاح، أنشئ قاعدة بيانات جديدة في مثيلك باسم registrants.
$ gcloud sql databases create registrants --instance codelab-instance
لقد أكملت الآن عملية إعداد مثيل Cloud SQL وقاعدة البيانات لتطبيقك.
5- إعداد تطبيق Spring Boot
نحن الآن جاهزون لبدء كتابة التطبيق. ستواصل الخطوات التالية استخدام Cloud Shell الموضّح في خطوات الإعداد.
أولاً، سنستخدم Initializr لإنشاء رمز التأسيس للمشروع. في نافذة Cloud Shell، نفِّذ الأمر التالي:
$ cd ~
$ curl https://start.spring.io/starter.tgz \
-d language=kotlin \
-d bootVersion=2.4.0 \
-d dependencies=web,data-jpa,integration,cloud-gcp-pubsub,thymeleaf \
-d baseDir=registrations-codelab | tar -xzvf -
$ cd registrations-codelab
ينشئ هذا الأمر عملية إعداد أولية لمشروع Maven بالإضافة إلى رمز أساسي لتطبيقك في الدليل registrations-codelab/. توضّح الأقسام التالية تعديلات الرموز البرمجية اللازمة لإنشاء تطبيق يعمل.
أداة تعديل الرموز البرمجية في Cloud Shell
أسهل طريقة لبدء تعديل الرمز وعرضه في بيئة Cloud Shell هي استخدام أداة تعديل الرموز البرمجية في Cloud Shell المضمّنة.
بعد فتح مثيل Cloud Shell، انقر على رمز القلم الرصاص لفتح أداة تعديل الرموز. يجب أن يتيح لك المحرّر تعديل ملفات المشروع التي ينتجها Initialzr مباشرةً.

6. إعداد قاعدة البيانات
أولاً، اضبط تطبيقك ليتمكّن من الاتصال بقاعدة بيانات Cloud MySQL التي أعددتها. توفّر مكتبات Spring Cloud GCP Cloud MySQL starter التي توفّر التبعيات اللازمة للاتصال بمثيل Cloud MySQL.
أضِف تبعية spring-cloud-gcp-starter-sql-mysql إلى ملف pom.xml الخاص بالمشروع:
registrations-codelab/pom.xml
...
<dependencies>
... Other dependencies above ...
<!-- Add the MySQL starter to the list of dependencies -->
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>spring-cloud-gcp-starter-sql-mysql</artifactId>
</dependency>
</dependencies>
بالإضافة إلى ذلك، عليك تعديل ملف الإعداد application.properties لوصف إعدادات قاعدة البيانات. انسخ السمات التالية في ملف application.properties.
ابحث عن اسم اتصال المثيل بقاعدة البيانات:
$ gcloud sql instances describe codelab-instance \ --format 'value(connectionName)'
سيتم استخدام ناتج هذا الأمر في ملف application.properties لضبط معلومات الربط.
src/main/resources/application.properties
# Modify this property using the output from the previous command line. spring.cloud.gcp.sql.instance-connection-name=INSTANCE_CONNECTION_NAME # Your database name spring.cloud.gcp.sql.database-name=registrants # So app starts despite "table already exists" errors. spring.datasource.continue-on-error=true # Enforces database initialization spring.datasource.initialization-mode=always # Cloud SQL (MySQL) only supports InnoDB, not MyISAM spring.jpa.database-platform=org.hibernate.dialect.MySQL55Dialect spring.jpa.hibernate.ddl-auto=create-drop # This is used if you want to connect to a different database instance # user other than root; not used in codelab. # spring.datasource.username=root # This is used to specify the password of the database user; # not used in codelab. # spring.datasource.password=password
الموقع الوحيد الذي يجب تعديله هو اسم الاتصال بالمثيل. يجب تنسيق هذه القيمة كقيمة مفصولة بنقطتين رأسيتين بالشكل التالي: YOUR_GCP_PROJECT_ID:REGION:DATABASE_INSTANCE_NAME.
7. إنشاء المحتوى الثابت
أولاً، سننشئ الواجهة الأمامية لتطبيقنا. يجب أن يتضمّن التطبيق نموذجًا يتيح للمستخدم تسجيل الأفراد، بالإضافة إلى طريقة عرض تعرض جميع المستخدمين الذين تم تسجيلهم بنجاح.
بالنسبة إلى الصفحة الرئيسية، أنشئ index.html يحتوي على نموذج التسجيل.
src/main/resources/static/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Registration Sample Application</title>
</head>
<body>
<h1>Registration</h1>
<div>
<nav>
<a href="/">Home</a><br>
<a href="/registrants">Registered People</a><br>
</nav>
<p>
This is a demo registration application which sends user information to a Pub/Sub topic and
persists it into a MySQL database.
</p>
<h2>Register Person</h2>
<div>
<form action="/registerPerson" method="post">
First Name: <input type="text" name="firstName" />
Last Name: <input type="text" name="lastName" />
Email: <input type="text" name="email" />
<input type="submit" value="Submit"/>
</form>
</div>
</div>
</body>
</html>
بعد ذلك، سننشئ نموذج Thymeleaf باسم registrants.html لعرض المستخدمين المسجّلين. Thymeleaf هو إطار عمل للنماذج نستخدمه لإنشاء وعرض ملفات HTML ديناميكية. ستلاحظ أنّ النموذج يشبه HTML، باستثناء أنّه يحتوي على بعض عناصر الترميز الإضافية للتعامل مع المحتوى الديناميكي. يقبل هذا النموذج مَعلمة واحدة تُسمّى personsList تحتوي على جميع المستخدمين الذين تم تسجيلهم من خلال التطبيق.
src/main/resources/templates/registrants.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Registrants List</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<h1>Registrants List</h1>
<p>
This page displays all the people who were registered through the Pub/Sub topic.
All results are retrieved from the MySQL database.
</p>
<table border="1">
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
</tr>
<tr th:each="person : ${personsList}">
<td>[[${person.firstName}]]</td>
<td>[[${person.lastName}]]</td>
<td>[[${person.email}]]</td>
</tr>
</table>
</body>
</html>
في هذه المرحلة، يمكنك التأكّد من أنّه يتم عرض المحتوى الثابت.
إنشاء التطبيق وتشغيله باستخدام Maven:
$ ./mvnw spring-boot:run
انقر على زر المعاينة في نافذة Cloud Shell وتأكَّد من ظهور الصفحة الرئيسية. ولن تعمل أي من الوظائف على واجهة المستخدم لأنّنا لا نملك أداة تحكّم على الويب. ستتم إضافة هذه المعلومات في الخطوة التالية.

بعد معاينة التطبيق، اضغط على CTRL+C لإنهاء التطبيق.
8. إرسال المسجّلين إلى موضوع Pub/Sub
في هذه الخطوة، سننفّذ الميزة التي يتم من خلالها نشر بيانات المسجّلين التي تم إرسالها من خلال نموذج الويب إلى موضوع Cloud Pub/Sub.
إضافة فئات البيانات
أولاً، سننشئ بعض فئات بيانات Kotlin، وستكون هذه الفئات هي عناصر JPA وستعمل أيضًا كتمثيل وسيط للمسجّلين الذين تم إرسالهم من خلال النموذج.
في حزمة العرض التوضيحي، أضِف ملفَين جديدَين: فئة Person وPersonRepository Spring Data. ستتيح لنا هاتان الفئتان تخزين إدخالات التسجيل واستردادها بسهولة من قاعدة بيانات MySQL باستخدام Spring Data JPA.
src/main/kotlin/com/example/demo/Person.kt
package com.example.demo
import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.Id
@Entity
data class Person(
val firstName: String,
val lastName: String,
val email: String,
@Id @GeneratedValue
var id: Long? = 0)
src/main/kotlin/com/example/demo/PersonRepository.kt
package com.example.demo
import org.springframework.data.repository.CrudRepository
interface PersonRepository : CrudRepository<Person, Long>
إضافة أداة التحكّم على الويب
بعد ذلك، سننشئ فئة Controller تعالج بيانات المسجّلين من النموذج وترسل المعلومات إلى موضوع Cloud Pub/Sub الذي أنشأته سابقًا. ينشئ وحدة التحكّم هذه نقطتَي نهاية:
/registerPerson: نقطة نهاية POST التي يتم فيها إرسال معلومات صاحب التسجيل ثم إرسالها إلى موضوع Pub/Sub. في الدالةregisterPerson(..)، يتم إرسال معلومات صاحب التسجيل إلى موضوع Pub/Sub باستخدامPubSubTemplate، وهو فئة ملائمة من عمليات الدمج في Spring Cloud GCP Pub/Sub التي تقلّل من رمز النص النموذجي اللازم لبدء التفاعل مع Cloud Pub/Sub./registrants: تعرض هذه السمة جميع المستخدمين المسجّلين بنجاح في قاعدة البيانات. يتم استرداد هذه المعلومات من مثيل MySQL باستخدام مستودع Spring Data الذي أنشأناه في الخطوة السابقة.
أنشئ فئة Controller التالية في حزمة العرض التوضيحي:
src/main/kotlin/com/example/demo/Controller.kt
package com.example.demo
import com.google.cloud.spring.pubsub.core.PubSubTemplate
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.servlet.ModelAndView
import org.springframework.web.servlet.view.RedirectView
@RestController
class Controller(val pubSubTemplate: PubSubTemplate, val personRepository: PersonRepository) {
// The Pub/Sub topic name created earlier.
val REGISTRATION_TOPIC = "registrations"
@PostMapping("/registerPerson")
fun registerPerson(
@RequestParam("firstName") firstName: String,
@RequestParam("lastName") lastName: String,
@RequestParam("email") email: String): RedirectView {
pubSubTemplate.publish(
REGISTRATION_TOPIC,
Person(firstName, lastName, email))
return RedirectView("/")
}
@GetMapping("/registrants")
fun getRegistrants(): ModelAndView {
val personsList = personRepository.findAll().toList()
return ModelAndView("registrants", mapOf("personsList" to personsList))
}
}
يقرأ عنصر التحكّم معلومات صاحب التسجيل التي تم إرسالها من خلال نموذج الويب، ثم ينشر المعلومات في موضوع Pub/Sub.
إضافة JSON Object Mapper Bean
ربما لاحظت في وحدة التحكّم أنّنا ننشر عنصر Person في موضوع Pub/Sub وليس سلسلة. ويكون ذلك ممكنًا لأنّنا نستفيد من إمكانية استخدام Spring Cloud GCP لحِمل JSON مخصّص يتم إرساله إلى المواضيع، إذ تتيح لك المكتبات تسلسل الكائنات إلى JSON وإرسال حِمل JSON إلى موضوع وإلغاء تسلسل الحِمل عند استلامه.
للاستفادة من هذه الميزة، يجب إضافة ObjectMapper bean إلى سياق تطبيقك. سيتم استخدام عنصر ObjectMapper هذا لتسلسل الكائنات من وإلى JSON عندما يرسل تطبيقك الرسائل ويتلقّاها. في فئة DemoApplication.kt، أضِف عنصر Spring JacksonPubSubMessageConverter على النحو التالي:
src/main/kotlin/com/example/demo/DemoApplication.kt
package com.example.demo
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
// new imports to add
import org.springframework.context.annotation.Bean
import com.fasterxml.jackson.databind.ObjectMapper
import com.google.cloud.spring.pubsub.support.converter.JacksonPubSubMessageConverter
@SpringBootApplication
class DemoApplication {
// This bean enables serialization/deserialization of
// Java objects to JSON for Pub/Sub payloads
@Bean
fun jacksonPubSubMessageConverter(objectMapper: ObjectMapper) =
JacksonPubSubMessageConverter(objectMapper)
}
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)
}
في هذه المرحلة، يمكنك محاولة تشغيل التطبيق مرة أخرى من خلال تنفيذ ما يلي:
$ ./mvnw spring-boot:run
من نموذج الويب على الصفحة الرئيسية، سيرسل التطبيق الآن المعلومات إلى موضوع Pub/Sub الذي أنشأته. ومع ذلك، لا يزال هذا التطبيق لا ينفّذ أي إجراء مفيد لأنّنا ما زلنا بحاجة إلى القراءة من موضوع Pub/Sub هذا. يتم إكمال هذه الخطوة في الخطوة التالية.
9- قراءة المسجّلين من موضوع Pub/Sub
في الخطوة الأخيرة، سنعالج معلومات المسجّلين من موضوع Pub/Sub ونحتفظ بالمعلومات في قاعدة بيانات Cloud MySQL. سيؤدي ذلك إلى إكمال التطبيق، ما يتيح لك إرسال مسجّلين جدد من خلال النموذج وعرض جميع المستخدمين المسجّلين من خلال نقطة النهاية /registrants.
سيستفيد هذا التطبيق من Spring Integration، الذي يوفّر العديد من التجريدات الملائمة للتعامل مع المراسلة. سنضيف PubSubInboundChannelAdapter لنتمكّن من قراءة الرسائل من موضوع Pub/Sub ووضعها في pubsubInputChannel لمزيد من المعالجة. سنقوم بعد ذلك بإعداد الدالة messageReceiver باستخدام @ServiceActivator ليتم استدعاؤها مع الرسائل الواردة على pubsubInputChannel.
src/main/kotlin/com/example/demo/DemoApplication.kt
package com.example.demo
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.context.annotation.Bean
import com.fasterxml.jackson.databind.ObjectMapper
import org.springframework.cloud.gcp.pubsub.support.converter.JacksonPubSubMessageConverter
// new imports to add
import com.google.cloud.spring.pubsub.core.PubSubTemplate
import com.google.cloud.spring.pubsub.integration.AckMode
import com.google.cloud.spring.pubsub.integration.inbound.PubSubInboundChannelAdapter
import com.google.cloud.spring.pubsub.support.BasicAcknowledgeablePubsubMessage
import com.google.cloud.spring.pubsub.support.GcpPubSubHeaders
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.integration.annotation.ServiceActivator
import org.springframework.integration.channel.DirectChannel
import org.springframework.messaging.MessageChannel
import org.springframework.messaging.handler.annotation.Header
@SpringBootApplication
class DemoApplication {
private val REGISTRANT_SUBSCRIPTION = "registrations-sub"
@Autowired
private lateinit var personRepository: PersonRepository
// New Spring Beans to add
@Bean
fun pubsubInputChannel() = DirectChannel()
@Bean
fun messageChannelAdapter(
@Qualifier("pubsubInputChannel") inputChannel: MessageChannel,
pubSubTemplate: PubSubTemplate): PubSubInboundChannelAdapter {
val adapter = PubSubInboundChannelAdapter(
pubSubTemplate, REGISTRANT_SUBSCRIPTION)
adapter.outputChannel = inputChannel
adapter.ackMode = AckMode.MANUAL
adapter.payloadType = Person::class.java
return adapter
}
@ServiceActivator(inputChannel = "pubsubInputChannel")
fun messageReceiver(
payload: Person,
@Header(GcpPubSubHeaders.ORIGINAL_MESSAGE) message: BasicAcknowledgeablePubsubMessage) {
personRepository.save(payload)
print("Message arrived! Payload: $payload")
message.ack()
}
// ObjectMapper bean from previous step
@Bean
fun jacksonPubSubMessageConverter(objectMapper: ObjectMapper) = JacksonPubSubMessageConverter(objectMapper)
}
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)
}
في هذه المرحلة، تكون قد أكملت عملية إعداد التطبيق. للتحقّق من عمل التطبيق بشكلٍ سليم، نفِّذ ما يلي:
$ ./mvnw spring-boot:run
انقر على الزر معاينة مرة أخرى وحاوِل تسجيل مستخدم من خلال ملء النموذج وإرساله.

انقر على الرابط المستخدمون المسجّلون للتأكّد من ظهور المستخدم الجديد في الجدول.

تهانينا، لقد انتهيت الآن. أنهِ التطبيق بالضغط على CTRL+C في نافذة الجهاز الطرفي.
10. تنظيف
لتنظيف بيئتك، عليك حذف موضوع Pub/Sub ومثيل Cloud MySQL اللذين أنشأتهما.
حذف مثيل Cloud MySQL
$ gcloud sql instances delete codelab-instance
حذف موارد Pub/Sub
$ gcloud pubsub subscriptions delete registrations-sub $ gcloud pubsub topics delete registrations
11. تهانينا!
لقد انتهيت الآن من كتابة تطبيق Spring Kotlin يتكامل مع Cloud Pub/Sub وCloud SQL (MySQL).
مزيد من المعلومات
- مشروع Spring on GCP: http://cloud.spring.io/spring-cloud-gcp/
- مستودع Spring on GCP GitHub: https://github.com/GoogleCloudPlatform/spring-cloud-gcp
- Java على Google Cloud Platform: https://cloud.google.com/java/
- أمثلة على تطبيقات Kotlin باستخدام Google Cloud Platform: https://github.com/GoogleCloudPlatform/spring-cloud-gcp/tree/master/spring-cloud-gcp-kotlin-samples
الترخيص
يخضع هذا العمل لترخيص المشاع الإبداعي مع نسب العمل إلى مؤلفه 2.0 Generic License.