├── .gitignore ├── LICENSE.md ├── README.md └── jee ├── README.md ├── build.gradle.groovy ├── build.gradle.kts ├── converted ├── build.gradle.groovy ├── build.gradle.kts └── src │ └── main │ ├── kotlin │ └── fables │ │ └── kotlin │ │ └── jee │ │ ├── Kitten.kt │ │ ├── business │ │ ├── KittenBusinessService.kt │ │ └── KittenEntity.kt │ │ └── rest │ │ ├── KittenRest.kt │ │ ├── KittenRestService.kt │ │ └── RestApplication.kt │ └── resources │ └── META-INF │ └── persistence.xml ├── gradle.properties ├── idiomatic ├── build.gradle.groovy ├── build.gradle.kts └── src │ └── main │ ├── kotlin │ └── fables │ │ └── kotlin │ │ └── jee │ │ ├── Kitten.kt │ │ ├── business │ │ ├── KittenBusinessService.kt │ │ └── KittenEntity.kt │ │ └── rest │ │ ├── KittenRest.kt │ │ ├── KittenRestService.kt │ │ └── RestApplication.kt │ └── resources │ └── META-INF │ └── persistence.xml ├── java ├── build.gradle.groovy ├── build.gradle.kts └── src │ ├── main │ ├── java │ │ └── fables │ │ │ └── kotlin │ │ │ └── jee │ │ │ ├── Kitten.java │ │ │ ├── business │ │ │ ├── KittenBusinessService.java │ │ │ └── KittenEntity.java │ │ │ └── rest │ │ │ ├── KittenRest.java │ │ │ ├── KittenRestService.java │ │ │ ├── KittenRestServiceConstructor.java │ │ │ └── RestApplication.java │ └── resources │ │ └── META-INF │ │ └── persistence.xml │ └── test │ └── java │ └── fables │ └── kotlin │ └── jee │ └── KittenTest.java ├── plugins ├── build.gradle.groovy ├── build.gradle.kts └── src │ └── main │ ├── kotlin │ └── fables │ │ └── kotlin │ │ └── jee │ │ ├── Kitten.kt │ │ ├── business │ │ ├── KittenBusinessService.kt │ │ └── KittenEntity.kt │ │ └── rest │ │ ├── KittenRest.kt │ │ ├── KittenRestService.kt │ │ └── RestApplication.kt │ └── resources │ └── META-INF │ └── persistence.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/ 3 | 4 | ### Intellij ### 5 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 6 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 7 | 8 | # User-specific stuff: 9 | .idea 10 | 11 | *.iml 12 | modules.xml 13 | 14 | target/** 15 | **/target/** 16 | 17 | ### Gradle ### 18 | .gradle 19 | /build/ 20 | 21 | # Ignore Gradle GUI config 22 | gradle-app.setting 23 | 24 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 25 | !gradle-wrapper.jar 26 | 27 | # Cache of project 28 | .gradletasknamecache 29 | 30 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 31 | # gradle/wrapper/gradle-wrapper.properties 32 | 33 | ### Node ### 34 | # Logs 35 | logs 36 | *.log 37 | npm-debug.log* 38 | 39 | # Runtime data 40 | pids 41 | *.pid 42 | *.seed 43 | *.pid.lock 44 | 45 | # Directory for instrumented libs generated by jscoverage/JSCover 46 | lib-cov 47 | 48 | # Coverage directory used by tools like istanbul 49 | coverage 50 | 51 | # nyc test coverage 52 | .nyc_output 53 | 54 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 55 | .grunt 56 | 57 | # node-waf configuration 58 | .lock-wscript 59 | 60 | # Compiled binary addons (http://nodejs.org/api/addons.html) 61 | build/Release 62 | 63 | # Dependency directories 64 | node_modules 65 | jspm_packages 66 | 67 | # Optional npm cache directory 68 | .npm 69 | 70 | # Optional eslint cache 71 | .eslintcache 72 | 73 | # Optional REPL history 74 | .node_repl_history 75 | 76 | # Output of 'npm pack' 77 | *.tgz 78 | 79 | 80 | ### SublimeText ### 81 | # cache files for sublime text 82 | *.tmlanguage.cache 83 | *.tmPreferences.cache 84 | *.stTheme.cache 85 | 86 | # workspace files are user-specific 87 | *.sublime-workspace 88 | 89 | # project files should be checked into the repository, unless a significant 90 | # proportion of contributors will probably not be using SublimeText 91 | # *.sublime-project 92 | 93 | # sftp configuration file 94 | sftp-config.json 95 | 96 | # Package control specific files 97 | Package Control.last-run 98 | Package Control.ca-list 99 | Package Control.ca-bundle 100 | Package Control.system-ca-bundle 101 | Package Control.cache/ 102 | Package Control.ca-certs/ 103 | bh_unicode_properties.cache 104 | 105 | # Sublime-github package stores a github token in this file 106 | # https://packagecontrol.io/packages/sublime-github 107 | GitHub.sublime-settings 108 | 109 | 110 | ### VisualStudioCode ### 111 | .vscode/* 112 | !.vscode/settings.json 113 | !.vscode/tasks.json 114 | !.vscode/launch.json 115 | !.vscode/extensions.json 116 | 117 | ### Other ### 118 | 119 | # misc 120 | /.sass-cache 121 | *.lock 122 | /coverage/* 123 | /typings 124 | *.lck 125 | 126 | # e2e 127 | /e2e/*.js 128 | /e2e/*.map 129 | 130 | #System Files 131 | .DS_Store 132 | Thumbs.db 133 | 134 | # compiled output 135 | /dist 136 | /tmp 137 | 138 | webapp/ 139 | swagger-codegen-cli-2.2.1.jar 140 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Zeljko Trogrlic 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fables 2 | Short stories about practical solutions. Include cute animals. 3 | 4 | Many times I tried to find minimal working solution for the problem that bothered me, but I would either get one-liner understandable only with experts in that area, or humonguous pile of code where needed parts were practically hidden. 5 | 6 | Fables contains collection of minimal buildable projects, containing only the critical parts to solve problem at hand. 7 | -------------------------------------------------------------------------------- /jee/README.md: -------------------------------------------------------------------------------- 1 | Java EE example shows how to convert simple Java Enterprise Edition project from Java to Kotlin. It contains 4 modules showing different 2 | stage of conversion: 3 | 4 | * *java:* pure Java code. 5 | * *converted:* converted code without any special Kotlin plugins. 6 | * *plugins:* Kotlin compiler and Jackson plugins. 7 | * *idiomatic:* idiomatic Kotlin code. 8 | 9 | ## Setting up the project 10 | 11 | Prepare IntelliJ IDEA 2017, JDK 8, Gradle, and Wildfly 10. 12 | 13 | Project uses Wildfly built-in ExampleDS data source. 14 | -------------------------------------------------------------------------------- /jee/build.gradle.groovy: -------------------------------------------------------------------------------- 1 | ext.kotlin_version = "1.1.1" 2 | ext.rest_assured_version = "2.9.0" 3 | 4 | allprojects { 5 | group = "fables.kotlin.jee" 6 | version = "1.0" 7 | } 8 | 9 | subprojects { 10 | 11 | repositories { 12 | mavenLocal() 13 | mavenCentral() 14 | } 15 | 16 | plugins.withType(WarPlugin) { 17 | dependencies { 18 | providedCompile("javax:javaee-api:7.0") 19 | } 20 | } 21 | 22 | tasks.withType(JavaCompile) { 23 | options.encoding = "UTF-8" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /jee/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.gradle.api.tasks.compile.JavaCompile 2 | 3 | allprojects { 4 | group = "fables.kotlin.jee" 5 | version = "1.0" 6 | 7 | repositories { 8 | gradleScriptKotlin() 9 | } 10 | 11 | } 12 | 13 | subprojects { 14 | 15 | repositories { 16 | mavenLocal() 17 | mavenCentral() 18 | } 19 | 20 | plugins.withType(WarPlugin::class.java) { 21 | dependencies { 22 | compileOnly("javax:javaee-api:7.0") 23 | } 24 | } 25 | 26 | tasks.withType(JavaCompile::class.java) { 27 | options.encoding = "UTF-8" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /jee/converted/build.gradle.groovy: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.jetbrains.kotlin.jvm").version("1.1.2") 3 | } 4 | 5 | apply { 6 | plugin("kotlin") 7 | plugin("war") 8 | } 9 | 10 | description = "Kotlin Server without Plugins" 11 | 12 | dependencies { 13 | compile ("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") // needed for ::class.java 14 | } 15 | -------------------------------------------------------------------------------- /jee/converted/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.gradle.api.tasks.bundling.War 2 | 3 | plugins { 4 | id("org.jetbrains.kotlin.jvm") version "1.1.2" 5 | } 6 | 7 | apply { 8 | plugin("kotlin") 9 | plugin("war") 10 | } 11 | 12 | (tasks["war"] as War).archiveName = "converted.war" 13 | 14 | /*tasks { 15 | "war"(War::class) { 16 | archiveName = "converted.war" 17 | } 18 | }*/ 19 | 20 | description = "Kotlin Server without Plugins" 21 | 22 | dependencies { 23 | compile("com.fasterxml.jackson.core:jackson-annotations:${extra["jackson_version"]}") 24 | compile("org.jetbrains.kotlin:kotlin-stdlib:${extra["kotlin_version"]}") // needed for ::class.java 25 | } 26 | -------------------------------------------------------------------------------- /jee/converted/src/main/kotlin/fables/kotlin/jee/Kitten.kt: -------------------------------------------------------------------------------- 1 | package fables.kotlin.jee 2 | 3 | /** 4 | * @author Zeljko Trogrlic 5 | */ 6 | interface Kitten { 7 | 8 | val name: String 9 | val cuteness: Int 10 | } 11 | -------------------------------------------------------------------------------- /jee/converted/src/main/kotlin/fables/kotlin/jee/business/KittenBusinessService.kt: -------------------------------------------------------------------------------- 1 | package fables.kotlin.jee.business 2 | 3 | import java.util.* 4 | import javax.ejb.Stateless 5 | import javax.persistence.EntityManager 6 | import javax.persistence.PersistenceContext 7 | 8 | /** 9 | * CRud kitten operations. 10 | * 11 | * @author Zeljko Trogrlic 12 | */ 13 | @Stateless 14 | open class KittenBusinessService { 15 | 16 | @PersistenceContext 17 | private lateinit var entityManager: EntityManager 18 | 19 | open fun add(kitten: KittenEntity): Int { 20 | entityManager.persist(kitten) 21 | return kitten.id!! 22 | } 23 | 24 | open fun find(id: Int): Optional = 25 | Optional.ofNullable(entityManager.find(KittenEntity::class.java, id)) 26 | } 27 | -------------------------------------------------------------------------------- /jee/converted/src/main/kotlin/fables/kotlin/jee/business/KittenEntity.kt: -------------------------------------------------------------------------------- 1 | package fables.kotlin.jee.business 2 | 3 | import fables.kotlin.jee.Kitten 4 | import javax.persistence.Entity 5 | import javax.persistence.GeneratedValue 6 | import javax.persistence.Id 7 | 8 | /** 9 | * Persistent kitten. 10 | * @author Zeljko Trogrlic 11 | */ 12 | @Entity 13 | data class KittenEntity private constructor( 14 | @Id 15 | @GeneratedValue 16 | var id: Int?, 17 | override var name: String, 18 | override var cuteness: Int // set Int.MAX_VALUE for Nermal 19 | ) : Kitten { 20 | 21 | private constructor() : this(null, "", 0) 22 | 23 | constructor(name: String, cuteness: Int) : this(null, name, cuteness) 24 | } 25 | -------------------------------------------------------------------------------- /jee/converted/src/main/kotlin/fables/kotlin/jee/rest/KittenRest.kt: -------------------------------------------------------------------------------- 1 | package fables.kotlin.jee.rest 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty 4 | import fables.kotlin.jee.Kitten 5 | 6 | /** 7 | * Kitten data transfer object. 8 | * On deserialization, default constructor properties cannot be mapped without @JsonProperty annotations. 9 | * @author Zeljko Trogrlic 10 | */ 11 | data class KittenRest( 12 | @param:JsonProperty("name") override val name: String, 13 | @param:JsonProperty("cuteness") override val cuteness: Int 14 | ) : Kitten 15 | -------------------------------------------------------------------------------- /jee/converted/src/main/kotlin/fables/kotlin/jee/rest/KittenRestService.kt: -------------------------------------------------------------------------------- 1 | package fables.kotlin.jee.rest 2 | 3 | import fables.kotlin.jee.business.KittenBusinessService 4 | import fables.kotlin.jee.business.KittenEntity 5 | import javax.inject.Inject 6 | import javax.ws.rs.* 7 | import javax.ws.rs.core.MediaType 8 | 9 | /** 10 | * JSON REST CRud service. 11 | * All non-private methods and variables (val and var) must be open. 12 | * @author Zeljko Trogrlic 13 | */ 14 | @Path("kitten") 15 | open class KittenRestService { 16 | 17 | /** 18 | * Value will be injected after the object is constructed, so it must be lateinit or nullable. 19 | * As it should always get value, lateinit is more appropriate. 20 | * It also means less null checks in code. 21 | */ 22 | @Inject 23 | private lateinit var kittenBusinessService: KittenBusinessService 24 | 25 | @GET 26 | @Path("{id}") 27 | @Produces(MediaType.APPLICATION_JSON) 28 | open fun find( 29 | @PathParam("id") id: Int 30 | ): KittenRest = kittenBusinessService 31 | .find(id) 32 | .map { kittenEntity -> KittenRest(kittenEntity.name, kittenEntity.cuteness) } 33 | .orElseThrow { NotFoundException("ID $id not found") } 34 | 35 | @POST 36 | @Produces(MediaType.APPLICATION_JSON) 37 | open fun add( 38 | kittenRest: KittenRest 39 | ): Int { 40 | val kittenEntity = KittenEntity(kittenRest.name, kittenRest.cuteness) 41 | return kittenBusinessService.add(kittenEntity) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /jee/converted/src/main/kotlin/fables/kotlin/jee/rest/RestApplication.kt: -------------------------------------------------------------------------------- 1 | package fables.kotlin.jee.rest 2 | 3 | import java.util.* 4 | import javax.ws.rs.ApplicationPath 5 | import javax.ws.rs.core.Application 6 | 7 | /** 8 | * REST application configuration. 9 | * @author Zeljko Trogrlic 10 | */ 11 | @ApplicationPath("api") 12 | class RestApplication : Application() { 13 | 14 | private val classes = HashSet>(Arrays.asList>(KittenRestService::class.java)) 15 | 16 | override fun getClasses(): Set> { 17 | return this.classes 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /jee/converted/src/main/resources/META-INF/persistence.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | org.hibernate.jpa.HibernatePersistenceProvider 7 | 8 | java:jboss/datasources/ExampleDS 9 | 10 | fables.kotlin.jee.business.KittenEntity 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /jee/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.script.lang.kotlin.accessors.auto=true 2 | kotlin_version=1.1.2 3 | jackson_version=2.8.6 4 | rest_assured_version=2.9.0 5 | -------------------------------------------------------------------------------- /jee/idiomatic/build.gradle.groovy: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.jetbrains.kotlin.jvm") version("1.1.1") 3 | id("org.jetbrains.kotlin.plugin.allopen") version("1.1.1") 4 | //id( "org.jetbrains.kotlin.plugin.jpa" ) version ( "1.1.1") 5 | id("org.jetbrains.kotlin.plugin.noarg") version("1.1.1") 6 | } 7 | 8 | apply { 9 | plugin("kotlin") 10 | plugin("kotlin-allopen") 11 | plugin("kotlin-jpa") 12 | plugin("kotlin-noarg") 13 | plugin("war") 14 | } 15 | 16 | description = "Kotlin Server with All Plugins" 17 | 18 | allOpen { 19 | annotation("javax.ws.rs.Path") 20 | annotation("javax.ejb.Stateless") 21 | } 22 | 23 | noArg { 24 | annotation("javax.ws.rs.Path") 25 | annotation("javax.ejb.Stateless") 26 | } 27 | 28 | dependencies { 29 | //compile("com.fasterxml.jackson.core:jackson-databind:${extra["jackson_version"]}") 30 | //compile("com.fasterxml.jackson.module:jackson-module-kotlin:${extra["jackson_version"]}") 31 | compile("org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version") 32 | 33 | runtimeOnly("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") 34 | } 35 | -------------------------------------------------------------------------------- /jee/idiomatic/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.gradle.api.tasks.bundling.War 2 | 3 | plugins { 4 | id("org.jetbrains.kotlin.jvm") version "1.1.2" 5 | id("org.jetbrains.kotlin.plugin.allopen") version "1.1.2" 6 | //id( "org.jetbrains.kotlin.plugin.jpa" ) version ( "1.1.1") 7 | id("org.jetbrains.kotlin.plugin.noarg") version "1.1.2" 8 | } 9 | 10 | apply { 11 | plugin("kotlin") 12 | plugin("kotlin-allopen") 13 | plugin("kotlin-jpa") 14 | plugin("kotlin-noarg") 15 | plugin("war") 16 | } 17 | 18 | description = "Kotlin Server with All Plugins" 19 | 20 | (tasks["war"] as War).archiveName = "idiomatic.war" 21 | 22 | allOpen { 23 | annotation("javax.ws.rs.Path") 24 | annotation("javax.ejb.Stateless") 25 | } 26 | 27 | noArg { 28 | annotation("javax.ws.rs.Path") 29 | annotation("javax.ejb.Stateless") 30 | } 31 | 32 | dependencies { 33 | compile("com.fasterxml.jackson.core:jackson-databind:${extra["jackson_version"]}") 34 | compile("com.fasterxml.jackson.module:jackson-module-kotlin:${extra["jackson_version"]}") 35 | compile("org.jetbrains.kotlin:kotlin-reflect:${extra["kotlin_version"]}") // must be "compile" for conflict resolution 36 | compile("org.jetbrains.kotlin:kotlin-stdlib-jre8:${extra["kotlin_version"]}") 37 | } 38 | -------------------------------------------------------------------------------- /jee/idiomatic/src/main/kotlin/fables/kotlin/jee/Kitten.kt: -------------------------------------------------------------------------------- 1 | package fables.kotlin.jee 2 | 3 | /** 4 | * @author Zeljko Trogrlic 5 | */ 6 | interface Kitten { 7 | 8 | val name: String 9 | val cuteness: Int 10 | } 11 | -------------------------------------------------------------------------------- /jee/idiomatic/src/main/kotlin/fables/kotlin/jee/business/KittenBusinessService.kt: -------------------------------------------------------------------------------- 1 | package fables.kotlin.jee.business 2 | 3 | import javax.ejb.Stateless 4 | import javax.persistence.EntityManager 5 | import javax.persistence.PersistenceContext 6 | 7 | /** 8 | * CRud kitten operations. 9 | * Class and all public methods are opened by Kotlin compiler "allopen" plugin. 10 | * @author Zeljko Trogrlic 11 | */ 12 | @Stateless 13 | class KittenBusinessService { 14 | 15 | @PersistenceContext 16 | private lateinit var entityManager: EntityManager 17 | 18 | operator fun plusAssign(kitten: KittenEntity) = 19 | entityManager.persist(kitten) // persist is void anyway; id will be updated 20 | 21 | operator fun get(id: Int): KittenEntity? = 22 | entityManager.find(KittenEntity::class.java, id) 23 | } 24 | -------------------------------------------------------------------------------- /jee/idiomatic/src/main/kotlin/fables/kotlin/jee/business/KittenEntity.kt: -------------------------------------------------------------------------------- 1 | package fables.kotlin.jee.business 2 | 3 | import fables.kotlin.jee.Kitten 4 | import javax.persistence.Entity 5 | import javax.persistence.GeneratedValue 6 | import javax.persistence.Id 7 | 8 | /** 9 | * Persistent kitten. 10 | * Class and all public methods are opened by Kotlin compiler "allopen" plugin. 11 | * Parameterless constructor generated by Kotlin compiler "jpa" plugin. 12 | * @author Zeljko Trogrlic 13 | */ 14 | @Entity 15 | data class KittenEntity private constructor( 16 | @Id 17 | @GeneratedValue 18 | var id: Int?, 19 | override var name: String, 20 | override var cuteness: Int // set Int.MAX_VALUE for Nermal 21 | ) : Kitten { 22 | 23 | constructor(name: String, cuteness: Int) : this(null, name, cuteness) 24 | } 25 | -------------------------------------------------------------------------------- /jee/idiomatic/src/main/kotlin/fables/kotlin/jee/rest/KittenRest.kt: -------------------------------------------------------------------------------- 1 | package fables.kotlin.jee.rest 2 | 3 | import fables.kotlin.jee.Kitten 4 | 5 | /** 6 | * Constructor parameter names mapped to JSON by jackson-module-kotlin. 7 | * @author Zeljko Trogrlic 8 | */ 9 | data class KittenRest( 10 | override val name: String, 11 | override val cuteness: Int 12 | ) : Kitten 13 | -------------------------------------------------------------------------------- /jee/idiomatic/src/main/kotlin/fables/kotlin/jee/rest/KittenRestService.kt: -------------------------------------------------------------------------------- 1 | package fables.kotlin.jee.rest 2 | 3 | import fables.kotlin.jee.business.KittenBusinessService 4 | import fables.kotlin.jee.business.KittenEntity 5 | import javax.inject.Inject 6 | import javax.ws.rs.* 7 | import javax.ws.rs.core.MediaType 8 | 9 | /** 10 | * JSON REST CRud service. 11 | * Class and all public methods are opened by Kotlin compiler plugin. 12 | * JEE will first create one noarg constructor instance, and then injected constructor instances. 13 | * @author Zeljko Trogrlic 14 | */ 15 | @Path("kitten") 16 | class KittenRestService @Inject constructor(private val kittenBusinessService: KittenBusinessService) { 17 | 18 | @GET 19 | @Path("{id}") 20 | @Produces(MediaType.APPLICATION_JSON) 21 | fun find( 22 | @PathParam("id") id: Int 23 | ): KittenRest = kittenBusinessService[id] 24 | ?.let { KittenRest(it.name, it.cuteness) } 25 | ?: throw NotFoundException("ID $id not found") // TIP: Do not forget to throw, otherwise 200 OK 26 | 27 | @POST 28 | @Produces(MediaType.APPLICATION_JSON) 29 | fun add( 30 | kittenRest: KittenRest 31 | ): Int = kittenRest 32 | .let { KittenEntity(it.name, it.cuteness) } 33 | .also { kittenBusinessService += it } 34 | .let { it.id!! } 35 | } 36 | -------------------------------------------------------------------------------- /jee/idiomatic/src/main/kotlin/fables/kotlin/jee/rest/RestApplication.kt: -------------------------------------------------------------------------------- 1 | package fables.kotlin.jee.rest 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper 4 | import com.fasterxml.jackson.module.kotlin.KotlinModule 5 | import javax.ws.rs.ApplicationPath 6 | import javax.ws.rs.core.Application 7 | import javax.ws.rs.ext.ContextResolver 8 | 9 | /** 10 | * REST application configuration. 11 | * Jakson KotlinModule is registered here. 12 | * @author Zeljko Trogrlic 13 | */ 14 | @ApplicationPath("api") 15 | class RestApplication : Application() { 16 | 17 | // TIP: it is not possible to override Java getter with Kotlin getter 18 | // This does not work: open val classes = setOf(KittenRestService::class.java) 19 | private val classes = setOf(KittenRestService::class.java) 20 | 21 | private val singletons = setOf(MyContextResolver()) 22 | 23 | override fun getClasses() = classes 24 | 25 | override fun getSingletons() = singletons 26 | } 27 | 28 | class MyContextResolver : ContextResolver { 29 | 30 | val objectMapper = ObjectMapper() 31 | 32 | init { 33 | objectMapper.registerModule(KotlinModule()) 34 | } 35 | 36 | override fun getContext(p0: Class<*>?) = objectMapper 37 | } 38 | -------------------------------------------------------------------------------- /jee/idiomatic/src/main/resources/META-INF/persistence.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | org.hibernate.jpa.HibernatePersistenceProvider 7 | 8 | java:jboss/datasources/ExampleDS 9 | 10 | fables.kotlin.jee.business.KittenEntity 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /jee/java/build.gradle.groovy: -------------------------------------------------------------------------------- 1 | apply { 2 | plugin( "war") 3 | } 4 | 5 | description = "Java Reference Server" 6 | 7 | sourceCompatibility = 1.8 8 | targetCompatibility = 1.8 9 | 10 | dependencies { 11 | compile ("com.fasterxml.jackson.core:jackson-annotations:$jackson_version") 12 | 13 | testCompile("com.fasterxml.jackson.core:jackson-core:${extra["jackson_version"]}") 14 | testCompile (group: "com.jayway.restassured", name: "rest-assured", version: rest_assured_version) 15 | testCompile (group: "junit", name: "junit", version: "4.12") 16 | testCompile (group: "org.hamcrest", name: "hamcrest-all", version: "1.3") 17 | } -------------------------------------------------------------------------------- /jee/java/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.gradle.api.tasks.bundling.War 2 | 3 | apply { 4 | plugin("war") 5 | } 6 | 7 | description = "Java Reference Server" 8 | 9 | (tasks["war"] as War).archiveName = "java.war" 10 | 11 | dependencies { 12 | compile("com.fasterxml.jackson.core:jackson-annotations:${extra["jackson_version"]}") 13 | 14 | testCompile("com.fasterxml.jackson.core:jackson-core:${extra["jackson_version"]}") 15 | testCompile("com.fasterxml.jackson.core:jackson-databind:${extra["jackson_version"]}") 16 | testCompile(group = "com.jayway.restassured", name = "rest-assured", version = extra["rest_assured_version"] as String?) 17 | testCompile(group = "junit", name = "junit", version = "4.12") 18 | testCompile(group = "org.hamcrest", name = "hamcrest-all", version = "1.3") 19 | } 20 | -------------------------------------------------------------------------------- /jee/java/src/main/java/fables/kotlin/jee/Kitten.java: -------------------------------------------------------------------------------- 1 | package fables.kotlin.jee; 2 | 3 | public interface Kitten { 4 | 5 | String getName(); 6 | 7 | int getCuteness(); 8 | } 9 | 10 | 11 | -------------------------------------------------------------------------------- /jee/java/src/main/java/fables/kotlin/jee/business/KittenBusinessService.java: -------------------------------------------------------------------------------- 1 | package fables.kotlin.jee.business; 2 | 3 | import javax.ejb.Stateless; 4 | import javax.persistence.EntityManager; 5 | import javax.persistence.PersistenceContext; 6 | import java.util.Optional; 7 | 8 | /** 9 | * CRud kitten operations. 10 | * 11 | * @author Zeljko Trogrlic 12 | */ 13 | @Stateless 14 | public class KittenBusinessService { 15 | 16 | @PersistenceContext 17 | protected EntityManager entityManager; 18 | 19 | public Integer add(KittenEntity kitten) { 20 | entityManager.persist(kitten); 21 | return kitten.getId(); 22 | } 23 | 24 | public Optional find(int id) { 25 | return Optional.ofNullable(entityManager.find(KittenEntity.class, id)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /jee/java/src/main/java/fables/kotlin/jee/business/KittenEntity.java: -------------------------------------------------------------------------------- 1 | package fables.kotlin.jee.business; 2 | 3 | import fables.kotlin.jee.Kitten; 4 | 5 | import javax.persistence.Entity; 6 | import javax.persistence.GeneratedValue; 7 | import javax.persistence.Id; 8 | 9 | /** 10 | * Persistent kitten. 11 | * 12 | * @author Zeljko Trogrlic 13 | */ 14 | @Entity 15 | public class KittenEntity 16 | implements Kitten { 17 | 18 | @Id 19 | @GeneratedValue 20 | private Integer id; 21 | private String name; 22 | private int cuteness; 23 | 24 | protected KittenEntity() { 25 | } 26 | 27 | public KittenEntity(String name, int cuteness) { 28 | this.name = name; 29 | this.cuteness = cuteness; 30 | } 31 | 32 | public Integer getId() { 33 | return this.id; 34 | } 35 | 36 | public void setId(Integer id) { 37 | this.id = id; 38 | } 39 | 40 | public String getName() { 41 | return this.name; 42 | } 43 | 44 | public void setName(String name) { 45 | this.name = name; 46 | } 47 | 48 | public int getCuteness() { 49 | return this.cuteness; 50 | } 51 | 52 | public void setCuteness(int cuteness) { 53 | this.cuteness = cuteness; 54 | } 55 | 56 | @Override 57 | public boolean equals(Object o) { 58 | if (this == o) { 59 | return true; 60 | } 61 | if (o == null || getClass() != o.getClass()) { 62 | return false; 63 | } 64 | 65 | KittenEntity that = (KittenEntity) o; 66 | 67 | if (cuteness != that.cuteness) { 68 | return false; 69 | } 70 | if (!id.equals(that.id)) { 71 | return false; 72 | } 73 | return name.equals(that.name); 74 | } 75 | 76 | @Override 77 | public int hashCode() { 78 | return id.hashCode(); 79 | } 80 | 81 | @Override 82 | public String toString() { 83 | return "KittenEntity{" + 84 | "id=" + id + 85 | ", name='" + name + '\'' + 86 | ", cuteness=" + cuteness + 87 | '}'; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /jee/java/src/main/java/fables/kotlin/jee/rest/KittenRest.java: -------------------------------------------------------------------------------- 1 | package fables.kotlin.jee.rest; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import fables.kotlin.jee.Kitten; 5 | 6 | /** 7 | * Kitten data transfer object. 8 | * 9 | * @author Zeljko Trogrlic 10 | */ 11 | public class KittenRest 12 | implements Kitten { 13 | 14 | private final String name; 15 | private final int cuteness; 16 | 17 | public String getName() { 18 | return this.name; 19 | } 20 | 21 | public int getCuteness() { 22 | return this.cuteness; 23 | } 24 | 25 | /** 26 | * Default constructor. 27 | * On deserialization, properties cannot be mapped without @JsonProperty annotations. 28 | */ 29 | public KittenRest( 30 | @JsonProperty("name") String name, 31 | @JsonProperty("cuteness") int cuteness 32 | ) { 33 | this.name = name; 34 | this.cuteness = cuteness; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /jee/java/src/main/java/fables/kotlin/jee/rest/KittenRestService.java: -------------------------------------------------------------------------------- 1 | package fables.kotlin.jee.rest; 2 | 3 | import fables.kotlin.jee.business.KittenBusinessService; 4 | import fables.kotlin.jee.business.KittenEntity; 5 | 6 | import javax.inject.Inject; 7 | import javax.ws.rs.*; 8 | 9 | /** 10 | * JSON REST CRud service. 11 | * JEE will first create one noarg instance, and then injected instances. 12 | * 13 | * @author Zeljko Trogrlic 14 | */ 15 | @Path("kitten") 16 | public class KittenRestService { 17 | 18 | @Inject 19 | protected KittenBusinessService kittenBusinessService; 20 | 21 | @GET 22 | @Path("{id}") 23 | @Produces({"application/json"}) 24 | public KittenRest find( 25 | @PathParam("id") final int id 26 | ) { 27 | return kittenBusinessService 28 | .find(id) 29 | .map(kittenEntity -> new KittenRest(kittenEntity.getName(), kittenEntity.getCuteness())) 30 | .orElseThrow(() -> new NotFoundException("ID " + id + " not found")); 31 | } 32 | 33 | @POST 34 | @Produces({"application/json"}) 35 | public Integer add(KittenRest kittenRest) { 36 | KittenEntity kittenEntity = new KittenEntity(kittenRest.getName(), kittenRest.getCuteness()); 37 | return kittenBusinessService.add(kittenEntity); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /jee/java/src/main/java/fables/kotlin/jee/rest/KittenRestServiceConstructor.java: -------------------------------------------------------------------------------- 1 | package fables.kotlin.jee.rest; 2 | 3 | import fables.kotlin.jee.business.KittenBusinessService; 4 | import fables.kotlin.jee.business.KittenEntity; 5 | 6 | import javax.inject.Inject; 7 | import javax.ws.rs.*; 8 | 9 | /** 10 | * Class demonstrates constructor injection. 11 | * Very clumsy in Java. 12 | * It is included for a reference; it is not used in the project. 13 | * 14 | * @author Zeljko Trogrlic 15 | */ 16 | @Path("kitten") 17 | public class KittenRestServiceConstructor { 18 | 19 | protected final KittenBusinessService kittenBusinessService; 20 | 21 | /** 22 | * Used once before first call. 23 | */ 24 | public KittenRestServiceConstructor() { 25 | kittenBusinessService = null; 26 | } 27 | 28 | /** 29 | * Used for each call. 30 | */ 31 | @Inject 32 | public KittenRestServiceConstructor(KittenBusinessService kittenBusinessService) { 33 | this.kittenBusinessService = kittenBusinessService; 34 | } 35 | 36 | @GET 37 | @Path("{id}") 38 | @Produces({"application/json"}) 39 | public KittenRest find( 40 | @PathParam("id") final int id 41 | ) { 42 | return kittenBusinessService 43 | .find(id) 44 | .map(kittenEntity -> new KittenRest(kittenEntity.getName(), kittenEntity.getCuteness())) 45 | .orElseThrow(() -> new NotFoundException("ID " + id + " not found")); 46 | } 47 | 48 | @POST 49 | @Produces({"application/json"}) 50 | public Integer add(KittenRest kittenRest) { 51 | KittenEntity kittenEntity = new KittenEntity(kittenRest.getName(), kittenRest.getCuteness()); 52 | return kittenBusinessService.add(kittenEntity); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /jee/java/src/main/java/fables/kotlin/jee/rest/RestApplication.java: -------------------------------------------------------------------------------- 1 | package fables.kotlin.jee.rest; 2 | 3 | import javax.ws.rs.ApplicationPath; 4 | import javax.ws.rs.core.Application; 5 | import java.util.Arrays; 6 | import java.util.HashSet; 7 | import java.util.Set; 8 | 9 | /** 10 | * REST application configuration. 11 | * 12 | * @author Zeljko Trogrlic 13 | */ 14 | @ApplicationPath("api") 15 | public class RestApplication 16 | extends Application { 17 | 18 | private final Set> classes = new HashSet<>(Arrays.asList(KittenRestService.class)); 19 | 20 | @Override 21 | public Set> getClasses() { 22 | return this.classes; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /jee/java/src/main/resources/META-INF/persistence.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | org.hibernate.jpa.HibernatePersistenceProvider 7 | 8 | java:jboss/datasources/ExampleDS 9 | 10 | fables.kotlin.jee.business.KittenEntity 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /jee/java/src/test/java/fables/kotlin/jee/KittenTest.java: -------------------------------------------------------------------------------- 1 | package fables.kotlin.jee; 2 | 3 | import com.jayway.restassured.RestAssured; 4 | import com.jayway.restassured.http.ContentType; 5 | import fables.kotlin.jee.rest.KittenRest; 6 | import org.junit.BeforeClass; 7 | import org.junit.Test; 8 | 9 | import javax.ws.rs.core.Response; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | import java.util.Optional; 13 | import java.util.UUID; 14 | 15 | import static org.junit.Assert.assertEquals; 16 | 17 | /** 18 | * @author Zeljko Trogrlic 19 | */ 20 | public class KittenTest { 21 | 22 | private Object OK = Response.Status.OK.getStatusCode(); 23 | 24 | @BeforeClass 25 | public static void setup() { 26 | RestAssured.port = Optional.ofNullable(System.getProperty("server.port")) 27 | .map(Integer::parseInt) 28 | .orElse(8080); 29 | String app = Optional.ofNullable(System.getProperty("server.app")) 30 | .orElse("java-1.0"); 31 | String defaultBase = app + "/api/kitten"; 32 | RestAssured.basePath = Optional.ofNullable(System.getProperty("server.base")) 33 | .orElse(defaultBase); 34 | RestAssured.baseURI = Optional.ofNullable(System.getProperty("server.host")) 35 | .orElse("http://localhost"); 36 | } 37 | 38 | @Test 39 | public void postAndGet() { 40 | 41 | final String name = UUID.randomUUID().toString(); 42 | final int cuteness = (int) (Math.random() * 100); 43 | Map kittenMap = new HashMap<>(2); 44 | kittenMap.put("name", name); 45 | kittenMap.put("cuteness", cuteness); 46 | 47 | String id = RestAssured.given() 48 | .contentType(ContentType.JSON) 49 | .body(kittenMap) 50 | .log().all() 51 | .when() 52 | .post() 53 | .prettyPeek() 54 | .then() 55 | .statusCode(200) 56 | .extract() 57 | .asString(); 58 | 59 | KittenRest kittenRest = 60 | RestAssured.given() 61 | .accept(ContentType.JSON) 62 | .when() 63 | .get("{id}", id) 64 | .then() 65 | .statusCode(200) 66 | .extract() 67 | .as(KittenRest.class); 68 | 69 | assertEquals(name, kittenRest.getName()); 70 | assertEquals(cuteness, kittenRest.getCuteness()); 71 | } 72 | 73 | @Test 74 | public void notFound() { 75 | 76 | RestAssured.given() 77 | .accept(ContentType.JSON) 78 | .when() 79 | .get("{id}", "-9999") 80 | .then() 81 | .statusCode(404); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /jee/plugins/build.gradle.groovy: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.jetbrains.kotlin.jvm") version "1.1.2" 3 | id("org.jetbrains.kotlin.plugin.allopen") version "1.1.2" 4 | //id( "org.jetbrains.kotlin.plugin.jpa" ) version "1.1.2" 5 | id("org.jetbrains.kotlin.plugin.noarg") version "1.1.2" 6 | } 7 | 8 | apply { 9 | plugin("kotlin") 10 | plugin("kotlin-allopen") 11 | plugin("kotlin-jpa") 12 | plugin("kotlin-noarg") 13 | plugin("war") 14 | } 15 | 16 | description = "Kotlin Server with All Plugins" 17 | 18 | allOpen { 19 | annotation("javax.ws.rs.Path") 20 | annotation("javax.ejb.Stateless") 21 | } 22 | 23 | noArg { 24 | annotation("javax.ws.rs.Path") 25 | annotation("javax.ejb.Stateless") 26 | } 27 | 28 | dependencies { 29 | compile("org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version") 30 | compile("com.fasterxml.jackson.module:jackson-module-kotlin:$jackson_version") 31 | //testCompile("com.fasterxml.jackson.core:jackson-annotations:${extra["jackson_version"]}") 32 | compile("com.fasterxml.jackson.core:jackson-databind:${extra["jackson_version"]}") 33 | runtimeOnly("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") 34 | 35 | testCompile("com.fasterxml.jackson.core:jackson-core:${extra["jackson_version"]}") 36 | } 37 | -------------------------------------------------------------------------------- /jee/plugins/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.gradle.api.tasks.bundling.War 2 | 3 | plugins { 4 | id("org.jetbrains.kotlin.jvm") version "1.1.2" 5 | id("org.jetbrains.kotlin.plugin.allopen") version ("1.1.2") 6 | //id( "org.jetbrains.kotlin.plugin.jpa" ) version ( "1.1.1") 7 | id("org.jetbrains.kotlin.plugin.noarg") version ("1.1.2") 8 | } 9 | 10 | apply { 11 | plugin("kotlin") 12 | plugin("kotlin-allopen") 13 | plugin("kotlin-jpa") 14 | plugin("kotlin-noarg") 15 | plugin("war") 16 | } 17 | 18 | description = "Kotlin Server with All Plugins" 19 | 20 | (tasks["war"] as War).archiveName = "plugins.war" 21 | 22 | allOpen { 23 | annotation("javax.ws.rs.Path") 24 | annotation("javax.ejb.Stateless") 25 | } 26 | 27 | noArg { 28 | annotation("javax.ws.rs.Path") 29 | annotation("javax.ejb.Stateless") 30 | } 31 | 32 | dependencies { 33 | compile("com.fasterxml.jackson.core:jackson-databind:${extra["jackson_version"]}") 34 | compile("com.fasterxml.jackson.module:jackson-module-kotlin:${extra["jackson_version"]}") 35 | compile("org.jetbrains.kotlin:kotlin-reflect:${extra["kotlin_version"]}") // must be "compile" for conflict resolution 36 | compile("org.jetbrains.kotlin:kotlin-stdlib-jre8:${extra["kotlin_version"]}") 37 | } 38 | -------------------------------------------------------------------------------- /jee/plugins/src/main/kotlin/fables/kotlin/jee/Kitten.kt: -------------------------------------------------------------------------------- 1 | package fables.kotlin.jee 2 | 3 | /** 4 | * @author Zeljko Trogrlic 5 | */ 6 | interface Kitten { 7 | 8 | val name: String 9 | val cuteness: Int 10 | } 11 | -------------------------------------------------------------------------------- /jee/plugins/src/main/kotlin/fables/kotlin/jee/business/KittenBusinessService.kt: -------------------------------------------------------------------------------- 1 | package fables.kotlin.jee.business 2 | 3 | import java.util.* 4 | import javax.ejb.Stateless 5 | import javax.persistence.EntityManager 6 | import javax.persistence.PersistenceContext 7 | 8 | /** 9 | * CRud kitten operations. 10 | * Class and all public methods are opened by Kotlin compiler "allopen" plugin. 11 | * @author Zeljko Trogrlic 12 | */ 13 | @Stateless 14 | class KittenBusinessService { 15 | 16 | @PersistenceContext 17 | private lateinit var entityManager: EntityManager 18 | 19 | fun add(kitten: KittenEntity): Int { 20 | entityManager.persist(kitten) 21 | return kitten.id!! 22 | } 23 | 24 | fun find(id: Int): Optional = 25 | Optional.ofNullable(entityManager.find(KittenEntity::class.java, id)) 26 | } 27 | -------------------------------------------------------------------------------- /jee/plugins/src/main/kotlin/fables/kotlin/jee/business/KittenEntity.kt: -------------------------------------------------------------------------------- 1 | package fables.kotlin.jee.business 2 | 3 | import fables.kotlin.jee.Kitten 4 | import javax.persistence.Entity 5 | import javax.persistence.GeneratedValue 6 | import javax.persistence.Id 7 | 8 | /** 9 | * Persistent kitten. 10 | * Class and all public methods are opened by Kotlin compiler "allopen" plugin. 11 | * Parameterless constructor generated by Kotlin compiler "jpa" plugin. 12 | * @author Zeljko Trogrlic 13 | */ 14 | @Entity 15 | data class KittenEntity private constructor( 16 | @Id 17 | @GeneratedValue 18 | var id: Int?, 19 | override var name: String, 20 | override var cuteness: Int // set Int.MAX_VALUE for Nermal 21 | ) : Kitten { 22 | 23 | constructor(name: String, cuteness: Int) : this(null, name, cuteness) 24 | } 25 | -------------------------------------------------------------------------------- /jee/plugins/src/main/kotlin/fables/kotlin/jee/rest/KittenRest.kt: -------------------------------------------------------------------------------- 1 | package fables.kotlin.jee.rest 2 | 3 | import fables.kotlin.jee.Kitten 4 | 5 | /** 6 | * Constructor parameter names mapped to JSON by jackson-module-kotlin. 7 | * @author Zeljko Trogrlic 8 | */ 9 | data class KittenRest( 10 | override val name: String, 11 | override val cuteness: Int 12 | ) : Kitten 13 | -------------------------------------------------------------------------------- /jee/plugins/src/main/kotlin/fables/kotlin/jee/rest/KittenRestService.kt: -------------------------------------------------------------------------------- 1 | package fables.kotlin.jee.rest 2 | 3 | import fables.kotlin.jee.business.KittenBusinessService 4 | import fables.kotlin.jee.business.KittenEntity 5 | import javax.inject.Inject 6 | import javax.ws.rs.* 7 | import javax.ws.rs.core.MediaType 8 | 9 | /** 10 | * JSON REST CRud service. 11 | * Class and all public methods are opened by Kotlin compiler plugin. 12 | * JEE will first create one noarg constructor instance, and then injected constructor instances. 13 | * @author Zeljko Trogrlic 14 | */ 15 | @Path("kitten") 16 | class KittenRestService @Inject constructor(private val kittenBusinessService: KittenBusinessService) { 17 | 18 | @GET 19 | @Path("{id}") 20 | @Produces(MediaType.APPLICATION_JSON) 21 | fun find( 22 | @PathParam("id") id: Int 23 | ): KittenRest = kittenBusinessService 24 | .find(id) 25 | .map { kittenEntity -> KittenRest(kittenEntity.name, kittenEntity.cuteness) } 26 | .orElseThrow { NotFoundException("ID $id not found") } 27 | 28 | @POST 29 | @Produces(MediaType.APPLICATION_JSON) 30 | fun add( 31 | kittenRest: KittenRest 32 | ): Int { 33 | val kittenEntity = KittenEntity(kittenRest.name, kittenRest.cuteness) 34 | return kittenBusinessService.add(kittenEntity) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /jee/plugins/src/main/kotlin/fables/kotlin/jee/rest/RestApplication.kt: -------------------------------------------------------------------------------- 1 | package fables.kotlin.jee.rest 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper 4 | import com.fasterxml.jackson.module.kotlin.KotlinModule 5 | import javax.ws.rs.ApplicationPath 6 | import javax.ws.rs.core.Application 7 | import javax.ws.rs.ext.ContextResolver 8 | 9 | /** 10 | * REST application configuration. 11 | * Jakson KotlinModule is registered here. 12 | * @author Zeljko Trogrlic 13 | */ 14 | @ApplicationPath("api") 15 | class RestApplication : Application() { 16 | 17 | // TIP: it is not possible to override Java getter with Kotlin getter 18 | // This does not work: open val classes = setOf(KittenRestService::class.java) 19 | private val classes = setOf(KittenRestService::class.java) 20 | 21 | private val singletons = setOf(MyContextResolver()) 22 | 23 | override fun getClasses() = classes 24 | 25 | override fun getSingletons() = singletons 26 | } 27 | 28 | class MyContextResolver : ContextResolver { 29 | 30 | val objectMapper = ObjectMapper() 31 | 32 | init { 33 | objectMapper.registerModule(KotlinModule()) 34 | } 35 | 36 | override fun getContext(p0: Class<*>?) = objectMapper 37 | } 38 | -------------------------------------------------------------------------------- /jee/plugins/src/main/resources/META-INF/persistence.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | org.hibernate.jpa.HibernatePersistenceProvider 7 | 8 | java:jboss/datasources/ExampleDS 9 | 10 | fables.kotlin.jee.business.KittenEntity 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /jee/settings.gradle: -------------------------------------------------------------------------------- 1 | def configureGradleScriptKotlinOn(ProjectDescriptor project) { 2 | project.buildFileName = 'build.gradle.kts' 3 | project.children.each { configureGradleScriptKotlinOn(it) } 4 | } 5 | 6 | rootProject.name = 'kotlin-jee' 7 | include 'java' 8 | include 'converted' 9 | include 'plugins' 10 | include 'idiomatic' 11 | 12 | //rootProject.buildFileName='build.gradle.kts' 13 | configureGradleScriptKotlinOn rootProject 14 | --------------------------------------------------------------------------------