See also:
Last active
February 22, 2023 01:04
-
-
Save nosix/93c32d0fd849df9fad31736f2ea4775a to your computer and use it in GitHub Desktop.
Google App Engine Standard Environment in Java8 with Kotlin and SpringBoot
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0"> | |
<!-- See: https://cloud.google.com/appengine/docs/standard/java/config/appref --> | |
<threadsafe>true</threadsafe> | |
<runtime>java8</runtime> | |
</appengine-web-app> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example | |
import com.example.mapper.json.JsonObjectMapper | |
import org.springframework.boot.SpringApplication | |
import org.springframework.boot.autoconfigure.SpringBootApplication | |
import org.springframework.boot.web.support.SpringBootServletInitializer | |
import org.springframework.context.annotation.Bean | |
import org.thymeleaf.spring4.SpringTemplateEngine | |
import org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver | |
import org.thymeleaf.spring4.view.ThymeleafViewResolver | |
@SpringBootApplication | |
class Application : SpringBootServletInitializer() { | |
/** | |
* JSON Object Mapper (for Request/Response) | |
*/ | |
@Bean | |
fun jsonObjectMapper() = JsonObjectMapper() | |
// Thymeleaf | |
private val TEMPLATE_MODE = "LOOSE_HTML5" | |
@Bean | |
fun viewResolver() = ThymeleafViewResolver().apply { | |
templateEngine = templateEngine() | |
} | |
@Bean | |
fun templateEngine() = SpringTemplateEngine().apply { | |
templateResolvers = setOf(templateResolver()) | |
} | |
@Bean | |
fun templateResolver() = SpringResourceTemplateResolver().apply { | |
templateMode = TEMPLATE_MODE | |
prefix = "classpath:/templates/" | |
suffix = ".html" | |
isCacheable = false // TODO: make it true before release | |
} | |
} | |
fun main(args: Array<String>) { | |
SpringApplication.run(Application::class.java, *args) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
buildscript { | |
ext.kotlin_version = '1.1.3' | |
ext.appengine_api = '1.9.54' | |
ext.spring_version = '1.5.3.RELEASE' | |
ext.jackson_version = '2.8.4' | |
repositories { | |
mavenCentral() | |
} | |
dependencies { | |
// Kotlin | |
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" | |
// Google App Engine + Spring Boot | |
classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version" | |
classpath "com.google.cloud.tools:appengine-gradle-plugin:+" | |
} | |
} | |
// Kotlin | |
apply plugin: 'kotlin' | |
// Google App Engine + Spring Boot | |
apply plugin: 'kotlin-spring' | |
apply plugin: 'war' | |
apply plugin: 'com.google.cloud.tools.appengine' | |
sourceCompatibility = 1.8 | |
targetCompatibility = 1.8 | |
appengine { // App Engine tasks configuration | |
//noinspection GroovyAssignabilityCheck | |
run { // local (dev_appserver) configuration (standard environments only) | |
port = 8080 // default | |
} | |
deploy { // deploy configuration | |
stopPreviousVersion = true // default - stop the current version | |
promote = true // default - & make this the current version | |
} | |
} | |
repositories { | |
mavenCentral() | |
} | |
dependencies { | |
// Kotlin | |
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version" | |
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" | |
// Google App Engine + Spring Boot | |
compile "com.google.appengine:appengine-api-1.0-sdk:$appengine_api" | |
compile("org.springframework.boot:spring-boot-starter-web:$spring_version") { | |
exclude module: 'jul-to-slf4j' | |
exclude module: 'spring-boot-starter-tomcat' | |
} | |
// Template Engine (Thymeleaf) | |
compile("org.springframework.boot:spring-boot-starter-thymeleaf:$spring_version") { | |
exclude module: 'tomcat-embed-core' | |
exclude module: 'tomcat-embed-el' | |
exclude module: 'tomcat-embed-websocket' | |
exclude module: 'groovy' | |
exclude module: 'jul-to-slf4j' | |
exclude module: 'spring-boot-starter-tomcat' | |
} | |
compile 'net.sourceforge.nekohtml:nekohtml:1.9.15' // for LEGACY HTML5 template | |
// JSON Object Mapper (for JSON Request/Response) | |
compile("com.fasterxml.jackson.module:jackson-module-kotlin:$jackson_version") { | |
exclude module: 'kotlin-reflect' | |
} | |
compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_version" | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.mapper.json | |
import com.fasterxml.jackson.databind.DeserializationFeature | |
import com.fasterxml.jackson.databind.ObjectMapper | |
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule | |
import com.fasterxml.jackson.module.kotlin.registerKotlinModule | |
import java.time.LocalDate | |
import java.time.LocalDateTime | |
import java.time.ZonedDateTime | |
class JsonObjectMapper : ObjectMapper() { | |
init { | |
disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) | |
registerKotlinModule() | |
registerModule(JavaTimeModule().apply { | |
addSerializer(ZonedDateTime::class.java, ZonedDateTimeSerializer()) | |
addDeserializer(ZonedDateTime::class.java, ZonedDateTimeDeserializer()) | |
addSerializer(LocalDate::class.java, LocalDateSerializer()) | |
addDeserializer(LocalDate::class.java, LocalDateDeserializer()) | |
addSerializer(LocalDateTime::class.java, LocalDateTimeSerializer()) | |
addDeserializer(LocalDateTime::class.java, LocalDateTimeDeserializer()) | |
}) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.mapper.json | |
import com.fasterxml.jackson.core.JsonParser | |
import com.fasterxml.jackson.databind.DeserializationContext | |
import com.fasterxml.jackson.databind.JsonDeserializer | |
import java.time.LocalDate | |
class LocalDateDeserializer : JsonDeserializer<LocalDate>() { | |
private val deserializer = ZonedDateTimeDeserializer() | |
override fun deserialize(p: JsonParser, ctx: DeserializationContext): LocalDate { | |
val dateTime = deserializer.deserialize(p, ctx) | |
return dateTime.toLocalDate() | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.mapper.json | |
import com.fasterxml.jackson.core.JsonGenerator | |
import com.fasterxml.jackson.databind.JsonSerializer | |
import com.fasterxml.jackson.databind.SerializerProvider | |
import java.time.LocalDate | |
import java.time.ZoneId | |
class LocalDateSerializer : JsonSerializer<LocalDate>() { | |
private val serializer = ZonedDateTimeSerializer() | |
override fun serialize(value: LocalDate, gen: JsonGenerator, serializers: SerializerProvider) { | |
val dateTime = value.atStartOfDay(ZoneId.systemDefault()) | |
serializer.serialize(dateTime, gen, serializers) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.mapper.json | |
import com.fasterxml.jackson.core.JsonParser | |
import com.fasterxml.jackson.databind.DeserializationContext | |
import com.fasterxml.jackson.databind.JsonDeserializer | |
import java.time.LocalDateTime | |
class LocalDateTimeDeserializer : JsonDeserializer<LocalDateTime>() { | |
private val deserializer = ZonedDateTimeDeserializer() | |
override fun deserialize(p: JsonParser, ctx: DeserializationContext): LocalDateTime { | |
return deserializer.deserialize(p, ctx).toLocalDateTime() | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.mapper.json | |
import com.fasterxml.jackson.core.JsonGenerator | |
import com.fasterxml.jackson.databind.JsonSerializer | |
import com.fasterxml.jackson.databind.SerializerProvider | |
import java.time.LocalDateTime | |
import java.time.ZoneId | |
class LocalDateTimeSerializer : JsonSerializer<LocalDateTime>() { | |
private val serializer = ZonedDateTimeSerializer() | |
override fun serialize(value: LocalDateTime, gen: JsonGenerator, serializers: SerializerProvider) { | |
serializer.serialize(value.atZone(ZoneId.systemDefault()), gen, serializers) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.mapper.json | |
import com.fasterxml.jackson.core.JsonParser | |
import com.fasterxml.jackson.databind.DeserializationContext | |
import com.fasterxml.jackson.databind.JsonDeserializer | |
import java.time.ZoneId | |
import java.time.ZonedDateTime | |
import java.time.format.DateTimeFormatter | |
class ZonedDateTimeDeserializer : JsonDeserializer<ZonedDateTime>() { | |
override fun deserialize(p: JsonParser, ctx: DeserializationContext): ZonedDateTime { | |
val utc = ZonedDateTime.parse(p.text, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX")) | |
return ZonedDateTime.ofInstant(utc.toInstant(), ZoneId.systemDefault()) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.mapper.json | |
import com.fasterxml.jackson.core.JsonGenerator | |
import com.fasterxml.jackson.databind.JsonSerializer | |
import com.fasterxml.jackson.databind.SerializerProvider | |
import java.time.ZoneId | |
import java.time.ZonedDateTime | |
import java.time.format.DateTimeFormatter | |
class ZonedDateTimeSerializer : JsonSerializer<ZonedDateTime>() { | |
override fun serialize(value: ZonedDateTime, gen: JsonGenerator, serializers: SerializerProvider) { | |
val utc = ZonedDateTime.ofInstant(value.toInstant(), ZoneId.of("UTC")) | |
gen.writeString(utc.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX"))) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment