├── docs ├── CNAME └── index.html ├── kilo-client ├── src │ ├── test │ │ ├── resources │ │ │ └── org │ │ │ │ └── httprpc │ │ │ │ └── kilo │ │ │ │ ├── io │ │ │ │ ├── detail.txt │ │ │ │ ├── upper.txt │ │ │ │ ├── comment.txt │ │ │ │ ├── embedded-xml.txt │ │ │ │ ├── invalid-path.txt │ │ │ │ ├── resource2.txt │ │ │ │ ├── conditional.txt │ │ │ │ ├── format1.txt │ │ │ │ ├── invalid-modifier.txt │ │ │ │ ├── master1.txt │ │ │ │ ├── inverted.txt │ │ │ │ ├── path.txt │ │ │ │ ├── repeating4.txt │ │ │ │ ├── repeating5.txt │ │ │ │ ├── resource1.txt │ │ │ │ ├── master2.txt │ │ │ │ ├── dictionary.txt │ │ │ │ ├── recursion.txt │ │ │ │ ├── iterate.csv │ │ │ │ ├── repeating7.txt │ │ │ │ ├── resource.properties │ │ │ │ ├── repeating6.txt │ │ │ │ ├── repeating1.txt │ │ │ │ ├── repeating3.txt │ │ │ │ ├── test.properties │ │ │ │ ├── default-content-type.txt │ │ │ │ ├── inheritance.txt │ │ │ │ ├── csv.properties │ │ │ │ ├── repeating2.txt │ │ │ │ └── format2.txt │ │ │ │ ├── xml │ │ │ │ ├── test.txt │ │ │ │ └── test.xml │ │ │ │ └── sql │ │ │ │ ├── test1.xml │ │ │ │ └── test2.xml │ │ └── java │ │ │ └── org │ │ │ └── httprpc │ │ │ └── kilo │ │ │ ├── sql │ │ │ ├── XMLTest.java │ │ │ ├── JSONTest.java │ │ │ └── TemporalAccessorTest.java │ │ │ ├── beans │ │ │ ├── TestRecord.java │ │ │ └── TestInterface.java │ │ │ ├── io │ │ │ ├── TextDecoderTest.java │ │ │ └── TextEncoderTest.java │ │ │ └── util │ │ │ ├── OptionalsTest.java │ │ │ ├── concurrent │ │ │ └── PipeTest.java │ │ │ └── CollectionsTest.java │ └── main │ │ └── java │ │ └── org │ │ └── httprpc │ │ └── kilo │ │ ├── util │ │ ├── package-info.java │ │ ├── concurrent │ │ │ ├── package-info.java │ │ │ ├── TimeoutException.java │ │ │ └── Pipe.java │ │ └── Optionals.java │ │ ├── xml │ │ └── package-info.java │ │ ├── beans │ │ └── package-info.java │ │ ├── io │ │ ├── package-info.java │ │ ├── NullWriter.java │ │ ├── TextDecoder.java │ │ ├── TextEncoder.java │ │ ├── BufferedReader.java │ │ ├── BufferedWriter.java │ │ ├── Encoder.java │ │ ├── Decoder.java │ │ └── PagedReader.java │ │ ├── sql │ │ ├── package-info.java │ │ ├── Numeric.java │ │ ├── Final.java │ │ ├── JSON.java │ │ ├── Table.java │ │ ├── Column.java │ │ ├── ForeignKey.java │ │ ├── Index.java │ │ ├── Identifier.java │ │ ├── PrimaryKey.java │ │ └── ResultSetAdapter.java │ │ ├── package-info.java │ │ ├── Required.java │ │ ├── ServicePath.java │ │ ├── RequestMethod.java │ │ ├── Name.java │ │ ├── ResourcePath.java │ │ ├── Description.java │ │ └── WebServiceException.java └── build.gradle ├── kilo.png ├── .idea ├── .gitignore ├── codeStyles │ └── codeStyleConfig.xml ├── vcs.xml ├── kotlinc.xml ├── sonarlint-state.xml ├── modules │ ├── kilo-test │ │ ├── Kilo.kilo-test.iml │ │ ├── Kilo.kilo-test.main.iml │ │ └── Kilo.kilo-test.test.iml │ ├── kilo-client │ │ ├── Kilo.kilo-client.iml │ │ ├── Kilo.kilo-client.main.iml │ │ └── Kilo.kilo-client.test.iml │ └── kilo-server │ │ ├── Kilo.kilo-server.iml │ │ ├── Kilo.kilo-server.main.iml │ │ └── Kilo.kilo-server.test.iml ├── sonarlint.xml ├── markdown.xml ├── compiler.xml ├── misc.xml ├── gradle.xml ├── dataSources.xml └── jarRepositories.xml ├── kilo-test ├── src │ ├── test │ │ ├── resources │ │ │ └── org │ │ │ │ └── httprpc │ │ │ │ └── kilo │ │ │ │ └── test │ │ │ │ ├── modifier.txt │ │ │ │ ├── test.txt │ │ │ │ ├── labels.properties │ │ │ │ ├── test.jpg │ │ │ │ ├── example.html │ │ │ │ ├── pets.csv │ │ │ │ ├── months.csv │ │ │ │ ├── breakfast-menu.html │ │ │ │ ├── pets.json │ │ │ │ ├── pets.xml │ │ │ │ ├── account.xml │ │ │ │ ├── months.json │ │ │ │ ├── pets.html │ │ │ │ ├── breakfast-menu.xslt │ │ │ │ ├── breakfast-menu.xml │ │ │ │ └── math.json │ │ └── java │ │ │ └── org │ │ │ └── httprpc │ │ │ └── kilo │ │ │ └── test │ │ │ ├── Weather.java │ │ │ ├── MemberServiceProxy.java │ │ │ ├── FilmServiceProxy.java │ │ │ ├── Vehicle.java │ │ │ ├── MathServiceProxy.java │ │ │ ├── Course.java │ │ │ ├── FilmsTest.java │ │ │ ├── CatalogServiceProxy.java │ │ │ ├── EmployeesTest.java │ │ │ ├── DocumentationTest.java │ │ │ ├── BulkUploadTest.java │ │ │ ├── XMLTransformTest.java │ │ │ ├── CatalogTest.java │ │ │ └── PetsTest.java │ └── main │ │ ├── resources │ │ ├── org │ │ │ └── httprpc │ │ │ │ └── kilo │ │ │ │ └── test │ │ │ │ ├── PetService.properties │ │ │ │ ├── pets.xml │ │ │ │ └── pets.html │ │ └── hibernate.cfg.xml │ │ ├── webapp │ │ └── WEB-INF │ │ │ └── web.xml │ │ └── java │ │ └── org │ │ └── httprpc │ │ └── kilo │ │ └── test │ │ ├── Row.java │ │ ├── AbstractDatabaseService.java │ │ ├── Owner.java │ │ ├── Coordinates.java │ │ ├── FilmActor.java │ │ ├── FilmCategory.java │ │ ├── GreetingService.java │ │ ├── Size.java │ │ ├── Rating.java │ │ ├── Category.java │ │ ├── Person.java │ │ ├── Pet.java │ │ ├── Actor.java │ │ ├── MemberService.java │ │ ├── Employee.java │ │ ├── FilmDetail.java │ │ ├── ItemDetail.java │ │ ├── Item.java │ │ ├── Film.java │ │ ├── MathService.java │ │ ├── BulkUploadService.java │ │ ├── HibernateEmployee.java │ │ ├── PetService.java │ │ ├── CatalogService.java │ │ ├── FilmService.java │ │ └── EmployeeService.java └── build.gradle ├── README ├── api-index.png └── catalog-api.png ├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── kilo-server ├── src │ └── main │ │ ├── resources │ │ └── org │ │ │ └── httprpc │ │ │ └── kilo │ │ │ ├── IndexServlet.properties │ │ │ ├── variable.html │ │ │ ├── IndexServlet_fr.properties │ │ │ ├── IndexServlet_de.properties │ │ │ ├── WebService_de.properties │ │ │ ├── WebService_es.properties │ │ │ ├── WebService_fr.properties │ │ │ ├── IndexServlet_es.properties │ │ │ ├── WebService.properties │ │ │ ├── type.html │ │ │ └── index.html │ │ └── java │ │ └── org │ │ └── httprpc │ │ └── kilo │ │ ├── FormData.java │ │ ├── Creates.java │ │ ├── Accepts.java │ │ └── IndexServlet.java └── build.gradle ├── .gitignore ├── gradle.properties ├── db.sql └── gradlew.bat /docs/CNAME: -------------------------------------------------------------------------------- 1 | httprpc.org -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/io/detail.txt: -------------------------------------------------------------------------------- 1 | {{.}} -------------------------------------------------------------------------------- /kilo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTTP-RPC/Kilo/HEAD/kilo.png -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/io/upper.txt: -------------------------------------------------------------------------------- 1 | {{.:upper}} -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/io/comment.txt: -------------------------------------------------------------------------------- 1 | >{{!comment}}< -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/io/embedded-xml.txt: -------------------------------------------------------------------------------- 1 | {{xml}} -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/io/invalid-path.txt: -------------------------------------------------------------------------------- 1 | {{a/b}} -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/io/resource2.txt: -------------------------------------------------------------------------------- 1 | {{$b}}{{$c}} -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/io/conditional.txt: -------------------------------------------------------------------------------- 1 | {{?a}}found{{/a}} -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/io/format1.txt: -------------------------------------------------------------------------------- 1 | {{.:format=%.2f}} -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/io/invalid-modifier.txt: -------------------------------------------------------------------------------- 1 | {{a:foo}} -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/io/master1.txt: -------------------------------------------------------------------------------- 1 | ({{>detail.txt}}) -------------------------------------------------------------------------------- /kilo-test/src/test/resources/org/httprpc/kilo/test/modifier.txt: -------------------------------------------------------------------------------- 1 | {{text:uppercase}} -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/io/inverted.txt: -------------------------------------------------------------------------------- 1 | {{^a}}not found{{/a}} -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/io/path.txt: -------------------------------------------------------------------------------- 1 | {{a/b}}{{a/d/e}}{{a/d/f}} -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/io/repeating4.txt: -------------------------------------------------------------------------------- 1 | [{{#.}}({{.}}){{/.}}] -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/io/repeating5.txt: -------------------------------------------------------------------------------- 1 | {{#.[,]}}{{.}}{{/.}} -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/io/resource1.txt: -------------------------------------------------------------------------------- 1 | {{$a}}{{>resource2.txt}} -------------------------------------------------------------------------------- /kilo-test/src/test/resources/org/httprpc/kilo/test/test.txt: -------------------------------------------------------------------------------- 1 | This is a test attachment. -------------------------------------------------------------------------------- /README/api-index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTTP-RPC/Kilo/HEAD/README/api-index.png -------------------------------------------------------------------------------- /README/catalog-api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTTP-RPC/Kilo/HEAD/README/catalog-api.png -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/io/master2.txt: -------------------------------------------------------------------------------- 1 | [{{#.}}({{>detail.txt}}){{/.}}] -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/io/dictionary.txt: -------------------------------------------------------------------------------- 1 | {a={{a}},b={{b}},c={{c}},d={{d}}} -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/io/recursion.txt: -------------------------------------------------------------------------------- 1 | [{{#.}}{{>recursion.txt}}{{/.}}] -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/io/iterate.csv: -------------------------------------------------------------------------------- 1 | a,b,c 2 | 1,2,3 3 | 4,5,6 4 | 7,8,9 5 | -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/io/repeating7.txt: -------------------------------------------------------------------------------- 1 | {{#entries[,]}}{{~}}:{{.}}{{/entries}} -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/io/resource.properties: -------------------------------------------------------------------------------- 1 | a = A1 2 | b = B2 3 | c = c 4 | -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/io/repeating6.txt: -------------------------------------------------------------------------------- 1 | {{#entries[,]}}{{~}}:{{value}}{{/entries}} -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'Kilo' 2 | 3 | include 'kilo-client', 'kilo-server', 'kilo-test' 4 | -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/io/repeating1.txt: -------------------------------------------------------------------------------- 1 | [{{#list}}{a={{a}},b={{b}},c={{c}}}{{/list}}] -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/io/repeating3.txt: -------------------------------------------------------------------------------- 1 | [{{#.}}[{{#.}}[{{#.}}{{a}}{{/.}}]{{/.}}]{{/.}}] -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTTP-RPC/Kilo/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/io/test.properties: -------------------------------------------------------------------------------- 1 | c = fh&i"j 2 | d = "klm\n" 3 | e = "n,\top" 4 | -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/xml/test.txt: -------------------------------------------------------------------------------- 1 | {{#map/list/item*[, ]}}{{@d}} {{.}}{{/map/list/item*}} -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/io/default-content-type.txt: -------------------------------------------------------------------------------- 1 | {{$c}} -------------------------------------------------------------------------------- /kilo-server/src/main/resources/org/httprpc/kilo/IndexServlet.properties: -------------------------------------------------------------------------------- 1 | title = Service Index 2 | services = Services 3 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /kilo-server/src/main/resources/org/httprpc/kilo/variable.html: -------------------------------------------------------------------------------- 1 | {{?type}}{{>type.html}}{{?required}} •{{/required}}{{/type}} -------------------------------------------------------------------------------- /kilo-server/src/main/resources/org/httprpc/kilo/IndexServlet_fr.properties: -------------------------------------------------------------------------------- 1 | title = Indice des Services 2 | services = Services 3 | -------------------------------------------------------------------------------- /kilo-test/src/test/resources/org/httprpc/kilo/test/labels.properties: -------------------------------------------------------------------------------- 1 | name = Name 2 | description = Description 3 | quantity = Quantity 4 | -------------------------------------------------------------------------------- /kilo-server/src/main/resources/org/httprpc/kilo/IndexServlet_de.properties: -------------------------------------------------------------------------------- 1 | title = Leistungsverzeichnis 2 | services = Dienstleistungen 3 | -------------------------------------------------------------------------------- /kilo-test/src/main/resources/org/httprpc/kilo/test/PetService.properties: -------------------------------------------------------------------------------- 1 | name = Name 2 | species = Species 3 | sex = Sex 4 | birth = Birth 5 | death = Death 6 | -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/io/inheritance.txt: -------------------------------------------------------------------------------- 1 | {{?d}}{{#list[,]}}{{a}}{{b/c}}{{.}}{{/list}} {{#map[,]}}{{a}}{{b/c}}{{.}}{{/map}}{{?e}}F{{/e}}{{/d}} -------------------------------------------------------------------------------- /kilo-test/src/test/resources/org/httprpc/kilo/test/test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTTP-RPC/Kilo/HEAD/kilo-test/src/test/resources/org/httprpc/kilo/test/test.jpg -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/io/csv.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTTP-RPC/Kilo/HEAD/kilo-client/src/test/resources/org/httprpc/kilo/io/csv.properties -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/sql/test1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | A 5 | B 6 | C 7 | 8 | -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/sql/test2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | D 5 | E 6 | F 7 | 8 | -------------------------------------------------------------------------------- /kilo-test/src/test/resources/org/httprpc/kilo/test/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

{{a}}

5 |

{{b}}

6 |

{{c}}

7 | 8 | 9 | -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/io/repeating2.txt: -------------------------------------------------------------------------------- 1 | {abc={{abc}},list1=[{{#list1}}{def={{def}},list2=[{{#list2}}{one={{one}},two={{two}},three={{three}}}{{/list2}}]{{/list1}}]} -------------------------------------------------------------------------------- /kilo-server/src/main/resources/org/httprpc/kilo/WebService_de.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTTP-RPC/Kilo/HEAD/kilo-server/src/main/resources/org/httprpc/kilo/WebService_de.properties -------------------------------------------------------------------------------- /kilo-server/src/main/resources/org/httprpc/kilo/WebService_es.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTTP-RPC/Kilo/HEAD/kilo-server/src/main/resources/org/httprpc/kilo/WebService_es.properties -------------------------------------------------------------------------------- /kilo-server/src/main/resources/org/httprpc/kilo/WebService_fr.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTTP-RPC/Kilo/HEAD/kilo-server/src/main/resources/org/httprpc/kilo/WebService_fr.properties -------------------------------------------------------------------------------- /kilo-server/src/main/resources/org/httprpc/kilo/IndexServlet_es.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTTP-RPC/Kilo/HEAD/kilo-server/src/main/resources/org/httprpc/kilo/IndexServlet_es.properties -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | .idea/data_source_mapping.xml 3 | .idea/dataSources 4 | .idea/dataSources.local.xml 5 | .idea/sqldialects.xml 6 | .idea/queries 7 | **/build 8 | **/out 9 | **/.DS_Store 10 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /kilo-test/src/test/resources/org/httprpc/kilo/test/pets.csv: -------------------------------------------------------------------------------- 1 | "Name","Species","Sex","Birth","Death" 2 | "Chirpy","bird","f","1998-09-11", 3 | "Claws","cat","m","1994-03-17", 4 | "Whistler","bird",,"1997-12-09", 5 | -------------------------------------------------------------------------------- /.idea/sonarlint-state.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1642951000525 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules/kilo-test/Kilo.kilo-test.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/modules/kilo-client/Kilo.kilo-client.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/modules/kilo-server/Kilo.kilo-server.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/modules/kilo-test/Kilo.kilo-test.main.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/modules/kilo-test/Kilo.kilo-test.test.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/modules/kilo-client/Kilo.kilo-client.main.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/modules/kilo-client/Kilo.kilo-client.test.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/modules/kilo-server/Kilo.kilo-server.main.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/modules/kilo-server/Kilo.kilo-server.test.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /kilo-test/src/test/resources/org/httprpc/kilo/test/months.csv: -------------------------------------------------------------------------------- 1 | "name","days" 2 | "January",31 3 | "February",28 4 | "March",31 5 | "April",30 6 | "May",31 7 | "June",30 8 | "July",31 9 | "August",31 10 | "September",30 11 | "October",31 12 | "November",30 13 | "December",31 -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /kilo-server/src/main/resources/org/httprpc/kilo/WebService.properties: -------------------------------------------------------------------------------- 1 | services = Services 2 | endpoints = Endpoints 3 | enumerations = Enumerations 4 | structures = Structures 5 | name = Name 6 | description = Description 7 | deprecated = Deprecated 8 | parameter = Parameter 9 | property = Property 10 | type = Type 11 | -------------------------------------------------------------------------------- /.idea/sonarlint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | -------------------------------------------------------------------------------- /kilo-test/src/main/resources/org/httprpc/kilo/test/pets.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{name}} 7 | {{birth}} 8 | {{death}} 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /kilo-server/src/main/resources/org/httprpc/kilo/type.html: -------------------------------------------------------------------------------- 1 | {{?intrinsic}}{{?iterable}}[{{?elementType}}{{>type.html}}{{/elementType}}]{{/iterable}}{{^iterable}}{{?map}}[{{?keyType}}{{>type.html}}{{/keyType}}: {{?valueType}}{{>type.html}}{{/valueType}}]{{/map}}{{^map}}{{name}}{{/map}}{{/iterable}}{{/intrinsic}}{{^intrinsic}}{{name}}{{/intrinsic}} -------------------------------------------------------------------------------- /kilo-test/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | Kilo Server Test 7 | 8 | -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/xml/test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | two 7 | 8 | 9 | abc 10 | déf 11 | ghi 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/markdown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | -------------------------------------------------------------------------------- /kilo-test/src/main/resources/hibernate.cfg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | org.hibernate.dialect.MariaDBDialect 10 | 11 | 12 | -------------------------------------------------------------------------------- /kilo-client/src/test/resources/org/httprpc/kilo/io/format2.txt: -------------------------------------------------------------------------------- 1 | {{timestamp:format=shortDate}}, 2 | {{timestamp:format=isoDateTime}}, 3 | {{date:format=shortDate}}, 4 | {{date:format=isoDateTime}}, 5 | {{instant:format=shortDate}}, 6 | {{instant:format=isoDateTime}}, 7 | {{localDate:format=shortDate}}, 8 | {{localTime:format=shortTime}}, 9 | {{localDateTime:format=shortDateTime}}, 10 | {{localDate:format=longDate}}, 11 | {{localTime:format=longTime}}, 12 | {{localDateTime:format=longDateTime}} -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | servletAPIVersion = 6.0.0 2 | connectorJVersion = 3.5.3 3 | junitVersion = 6.0.1 4 | 5 | projectDescription = Lightweight REST for Java 6 | projectURL = https://github.com/HTTP-RPC/Kilo 7 | licenseName = The Apache License, Version 2.0 8 | licenseURL = https://www.apache.org/licenses/LICENSE-2.0.txt 9 | developerName = Greg Brown 10 | developerEmail = gk_brown@icloud.com 11 | scmConnection = scm:git:git://github.com/HTTP-RPC/Kilo.git 12 | scmURL = https://github.com/HTTP-RPC/Kilo 13 | 14 | org.gradle.configuration-cache = true 15 | -------------------------------------------------------------------------------- /kilo-test/src/test/resources/org/httprpc/kilo/test/breakfast-menu.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | {{name}} - {{price}} 7 |
8 |
9 |

{{description}} ({{calories}} calories per serving)

10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /kilo-test/src/main/resources/org/httprpc/kilo/test/pets.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
{{$name}}{{$species}}{{$sex}}{{$birth}}{{$death}}
{{name}}{{species}}{{sex}}{{birth}}{{death}}
23 | 24 | 25 | -------------------------------------------------------------------------------- /kilo-test/src/test/resources/org/httprpc/kilo/test/pets.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "birth": "1998-09-11", 4 | "death": null, 5 | "name": "Chirpy", 6 | "owner": "Gwen", 7 | "sex": "f", 8 | "species": "bird" 9 | }, 10 | { 11 | "birth": "1994-03-17", 12 | "death": null, 13 | "name": "Claws", 14 | "owner": "Gwen", 15 | "sex": "m", 16 | "species": "cat" 17 | }, 18 | { 19 | "birth": "1997-12-09", 20 | "death": null, 21 | "name": "Whistler", 22 | "owner": "Gwen", 23 | "sex": null, 24 | "species": "bird" 25 | } 26 | ] 27 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | -------------------------------------------------------------------------------- /kilo-test/src/test/resources/org/httprpc/kilo/test/pets.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Chirpy 7 | 1998-09-11 8 | 9 | 10 | 11 | 12 | Claws 13 | 1994-03-17 14 | 15 | 16 | 17 | 18 | Whistler 19 | 1997-12-09 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/util/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | /** 16 | * Contains common utility classes. 17 | */ 18 | package org.httprpc.kilo.util; -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/xml/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | /** 16 | * Contains classes for working with XML data. 17 | */ 18 | package org.httprpc.kilo.xml; -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/beans/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | /** 16 | * Contains classes for working with Java bean types. 17 | */ 18 | package org.httprpc.kilo.beans; -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/io/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | /** 16 | * Contains classes for working with common file formats. 17 | */ 18 | package org.httprpc.kilo.io; -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/sql/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | /** 16 | * Contains classes for interacting with relational databases. 17 | */ 18 | package org.httprpc.kilo.sql; -------------------------------------------------------------------------------- /kilo-test/src/test/resources/org/httprpc/kilo/test/account.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | John 6 | Smith 7 | 8 | 9 | 10 | 100.00 11 | 10/5/2024 12 | 13 | 14 | 50.00 15 | 10/12/2024 16 | 17 | 18 | 25.00 19 | 10/14/2024 20 | 21 | 22 | 75.00 23 | 10/19/2024 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/util/concurrent/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | /** 16 | * Contains classes for working with multiple threads. 17 | */ 18 | package org.httprpc.kilo.util.concurrent; -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | /** 16 | * Contains classes for creating and consuming RESTful and REST-like web 17 | * services. 18 | */ 19 | package org.httprpc.kilo; -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /kilo-test/src/test/resources/org/httprpc/kilo/test/months.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "January", 4 | "days": 31 5 | }, 6 | { 7 | "name": "February", 8 | "days": 28 9 | }, 10 | { 11 | "name": "March", 12 | "days": 31 13 | }, 14 | { 15 | "name": "April", 16 | "days": 30 17 | }, 18 | { 19 | "name": "May", 20 | "days": 31 21 | }, 22 | { 23 | "name": "June", 24 | "days": 30 25 | }, 26 | { 27 | "name": "July", 28 | "days": 31 29 | }, 30 | { 31 | "name": "August", 32 | "days": 31 33 | }, 34 | { 35 | "name": "September", 36 | "days": 30 37 | }, 38 | { 39 | "name": "October", 40 | "days": 31 41 | }, 42 | { 43 | "name": "November", 44 | "days": 30 45 | }, 46 | { 47 | "name": "December", 48 | "days": 31 49 | } 50 | ] -------------------------------------------------------------------------------- /kilo-test/src/main/java/org/httprpc/kilo/test/Row.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | public record Row( 18 | String text1, 19 | String text2, 20 | Integer number1, 21 | Double number2, 22 | Double number3 23 | ) { 24 | } 25 | -------------------------------------------------------------------------------- /kilo-test/src/test/resources/org/httprpc/kilo/test/pets.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 |
NameSpeciesSexBirthDeath
Chirpybirdf1998-09-11
Clawscatm1994-03-17
Whistlerbird1997-12-09
39 | 40 | 41 | -------------------------------------------------------------------------------- /.idea/dataSources.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | mariadb 6 | true 7 | org.mariadb.jdbc.Driver 8 | jdbc:mariadb://db.local:3306 9 | 10 | 11 | 12 | 13 | 14 | $ProjectFileDir$ 15 | 16 | 17 | -------------------------------------------------------------------------------- /kilo-test/src/test/java/org/httprpc/kilo/test/Weather.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import java.time.LocalDate; 18 | 19 | public interface Weather { 20 | LocalDate getDate(); 21 | String getConditions(); 22 | double getHigh(); 23 | double getLow(); 24 | } 25 | -------------------------------------------------------------------------------- /kilo-client/src/test/java/org/httprpc/kilo/sql/XMLTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.sql; 16 | 17 | import org.w3c.dom.Document; 18 | 19 | @Table("xml_test") 20 | public record XMLTest( 21 | @Column("id") 22 | @PrimaryKey 23 | Integer id, 24 | @Column("xml") 25 | Document document 26 | ) { 27 | } 28 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/sql/Numeric.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.sql; 16 | 17 | /** 18 | * Indicates that an enum type is numeric. 19 | */ 20 | public interface Numeric { 21 | /** 22 | * @return 23 | * The numeric value associated with the enum. 24 | */ 25 | int value(); 26 | } 27 | -------------------------------------------------------------------------------- /kilo-test/src/test/resources/org/httprpc/kilo/test/breakfast-menu.xslt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | - 8 | 9 |
10 |
11 |

12 | 13 | ( calories per serving) 14 |

15 |
16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /kilo-test/src/main/java/org/httprpc/kilo/test/AbstractDatabaseService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import org.httprpc.kilo.WebService; 18 | 19 | public abstract class AbstractDatabaseService extends WebService { 20 | @Override 21 | protected String getDataSourceName() { 22 | return "java:comp/env/jdbc/DemoDB"; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /kilo-client/src/test/java/org/httprpc/kilo/beans/TestRecord.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.beans; 16 | 17 | import org.httprpc.kilo.Name; 18 | import org.httprpc.kilo.Required; 19 | 20 | import java.time.LocalDate; 21 | 22 | public record TestRecord( 23 | int i, 24 | double d, 25 | @Name("s") 26 | @Required 27 | String string, 28 | LocalDate localDate) { 29 | } 30 | -------------------------------------------------------------------------------- /kilo-test/src/main/java/org/httprpc/kilo/test/Owner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import org.httprpc.kilo.sql.Column; 18 | import org.httprpc.kilo.sql.Index; 19 | import org.httprpc.kilo.sql.PrimaryKey; 20 | import org.httprpc.kilo.sql.Table; 21 | 22 | @Table("owner") 23 | public interface Owner { 24 | @Column("name") 25 | @PrimaryKey 26 | @Index 27 | String getName(); 28 | } 29 | -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/sql/Final.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.sql; 16 | 17 | import java.lang.annotation.ElementType; 18 | import java.lang.annotation.Retention; 19 | import java.lang.annotation.RetentionPolicy; 20 | import java.lang.annotation.Target; 21 | 22 | /** 23 | * Indicates that a property is final. 24 | */ 25 | @Retention(RetentionPolicy.RUNTIME) 26 | @Target(ElementType.METHOD) 27 | public @interface Final { 28 | } 29 | -------------------------------------------------------------------------------- /kilo-test/src/main/java/org/httprpc/kilo/test/Coordinates.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import org.httprpc.kilo.Description; 18 | import org.httprpc.kilo.Required; 19 | 20 | @Description("Represents an x/y coordinate pair.") 21 | public record Coordinates( 22 | @Description("The x-coordinate.") 23 | @Required 24 | int x, 25 | 26 | @Description("The y-coordinate.") 27 | @Required 28 | int y 29 | ) { 30 | } 31 | -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/sql/JSON.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.sql; 16 | 17 | import java.lang.annotation.ElementType; 18 | import java.lang.annotation.Retention; 19 | import java.lang.annotation.RetentionPolicy; 20 | import java.lang.annotation.Target; 21 | 22 | /** 23 | * Indicates that a property is encoded as JSON. 24 | */ 25 | @Retention(RetentionPolicy.RUNTIME) 26 | @Target(ElementType.METHOD) 27 | public @interface JSON { 28 | } 29 | -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/io/NullWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.io; 16 | 17 | import java.io.Writer; 18 | 19 | class NullWriter extends Writer { 20 | @Override 21 | public void write(char[] cbuf, int off, int len) { 22 | // No-op 23 | } 24 | 25 | @Override 26 | public void flush() { 27 | // No-op 28 | } 29 | 30 | @Override 31 | public void close() { 32 | // No-op 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /kilo-server/src/main/java/org/httprpc/kilo/FormData.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo; 16 | 17 | import java.lang.annotation.ElementType; 18 | import java.lang.annotation.Retention; 19 | import java.lang.annotation.RetentionPolicy; 20 | import java.lang.annotation.Target; 21 | 22 | /** 23 | * Indicates that a service method accepts form data. 24 | */ 25 | @Retention(RetentionPolicy.RUNTIME) 26 | @Target(ElementType.METHOD) 27 | public @interface FormData { 28 | } 29 | -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/Required.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo; 16 | 17 | import java.lang.annotation.ElementType; 18 | import java.lang.annotation.Retention; 19 | import java.lang.annotation.RetentionPolicy; 20 | import java.lang.annotation.Target; 21 | 22 | /** 23 | * Indicates that an element is required. 24 | */ 25 | @Retention(RetentionPolicy.RUNTIME) 26 | @Target({ElementType.METHOD, ElementType.PARAMETER}) 27 | public @interface Required { 28 | } 29 | -------------------------------------------------------------------------------- /kilo-server/src/main/java/org/httprpc/kilo/Creates.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo; 16 | 17 | import java.lang.annotation.ElementType; 18 | import java.lang.annotation.Retention; 19 | import java.lang.annotation.RetentionPolicy; 20 | import java.lang.annotation.Target; 21 | 22 | /** 23 | * Indicates that a service method results in creation of a resource. 24 | */ 25 | @Retention(RetentionPolicy.RUNTIME) 26 | @Target(ElementType.METHOD) 27 | public @interface Creates { 28 | } 29 | -------------------------------------------------------------------------------- /kilo-test/src/main/java/org/httprpc/kilo/test/FilmActor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import org.httprpc.kilo.sql.Column; 18 | import org.httprpc.kilo.sql.ForeignKey; 19 | import org.httprpc.kilo.sql.Table; 20 | 21 | @Table("film_actor") 22 | public record FilmActor( 23 | @Column("film_id") 24 | @ForeignKey(Film.class) 25 | Integer filmID, 26 | 27 | @Column("actor_id") 28 | @ForeignKey(Actor.class) 29 | Integer actorID 30 | ) { 31 | } 32 | -------------------------------------------------------------------------------- /kilo-test/src/main/java/org/httprpc/kilo/test/FilmCategory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import org.httprpc.kilo.sql.Column; 18 | import org.httprpc.kilo.sql.ForeignKey; 19 | import org.httprpc.kilo.sql.Table; 20 | 21 | @Table("film_category") 22 | public record FilmCategory( 23 | @Column("film_id") 24 | @ForeignKey(Film.class) 25 | Integer filmID, 26 | 27 | @Column("category_id") 28 | @ForeignKey(Category.class) 29 | Integer categoryID 30 | ) { 31 | } 32 | -------------------------------------------------------------------------------- /kilo-server/src/main/java/org/httprpc/kilo/Accepts.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo; 16 | 17 | import java.lang.annotation.ElementType; 18 | import java.lang.annotation.Retention; 19 | import java.lang.annotation.RetentionPolicy; 20 | import java.lang.annotation.Target; 21 | 22 | /** 23 | * Indicates that a service method may not immediately fulfill submitted 24 | * requests. 25 | */ 26 | @Retention(RetentionPolicy.RUNTIME) 27 | @Target(ElementType.METHOD) 28 | public @interface Accepts { 29 | } 30 | -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/util/concurrent/TimeoutException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.util.concurrent; 16 | 17 | /** 18 | * Thrown to indicate that a blocking operation timed out. 19 | */ 20 | public class TimeoutException extends RuntimeException { 21 | /** 22 | * Constructs a new timeout exception. 23 | * 24 | * @param message 25 | * The detail message. 26 | */ 27 | public TimeoutException(String message) { 28 | super(message); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/sql/Table.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.sql; 16 | 17 | import java.lang.annotation.ElementType; 18 | import java.lang.annotation.Retention; 19 | import java.lang.annotation.RetentionPolicy; 20 | import java.lang.annotation.Target; 21 | 22 | /** 23 | * Associates a table name with an entity type. 24 | */ 25 | @Retention(RetentionPolicy.RUNTIME) 26 | @Target(ElementType.TYPE) 27 | public @interface Table { 28 | /** 29 | * The table name. 30 | */ 31 | String value(); 32 | } 33 | -------------------------------------------------------------------------------- /kilo-test/src/test/java/org/httprpc/kilo/test/MemberServiceProxy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import org.httprpc.kilo.Name; 18 | import org.httprpc.kilo.RequestMethod; 19 | import org.httprpc.kilo.ServicePath; 20 | 21 | import java.io.IOException; 22 | import java.util.List; 23 | 24 | @ServicePath("members") 25 | public interface MemberServiceProxy { 26 | @RequestMethod("GET") 27 | List getMembers(@Name("first_name") String firstName, @Name("last_name") String lastName) throws IOException; 28 | } 29 | -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/sql/Column.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.sql; 16 | 17 | import java.lang.annotation.ElementType; 18 | import java.lang.annotation.Retention; 19 | import java.lang.annotation.RetentionPolicy; 20 | import java.lang.annotation.Target; 21 | 22 | /** 23 | * Associates a column name with a property. 24 | */ 25 | @Retention(RetentionPolicy.RUNTIME) 26 | @Target(ElementType.METHOD) 27 | public @interface Column { 28 | /** 29 | * The column name. 30 | */ 31 | String value(); 32 | } 33 | -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/ServicePath.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo; 16 | 17 | import java.lang.annotation.ElementType; 18 | import java.lang.annotation.Retention; 19 | import java.lang.annotation.RetentionPolicy; 20 | import java.lang.annotation.Target; 21 | 22 | /** 23 | * Associates a service path with a proxy type. 24 | */ 25 | @Retention(RetentionPolicy.RUNTIME) 26 | @Target(ElementType.TYPE) 27 | public @interface ServicePath { 28 | /** 29 | * The type's service path. 30 | */ 31 | String value(); 32 | } 33 | -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/RequestMethod.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo; 16 | 17 | import java.lang.annotation.ElementType; 18 | import java.lang.annotation.Retention; 19 | import java.lang.annotation.RetentionPolicy; 20 | import java.lang.annotation.Target; 21 | 22 | /** 23 | * Associates an HTTP verb with a service method. 24 | */ 25 | @Retention(RetentionPolicy.RUNTIME) 26 | @Target(ElementType.METHOD) 27 | public @interface RequestMethod { 28 | /** 29 | * The method's HTTP verb. 30 | */ 31 | String value(); 32 | } 33 | -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/Name.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo; 16 | 17 | import java.lang.annotation.ElementType; 18 | import java.lang.annotation.Retention; 19 | import java.lang.annotation.RetentionPolicy; 20 | import java.lang.annotation.Target; 21 | 22 | /** 23 | * Associates a custom name with an element. 24 | */ 25 | @Retention(RetentionPolicy.RUNTIME) 26 | @Target({ElementType.METHOD, ElementType.PARAMETER}) 27 | public @interface Name { 28 | /** 29 | * The custom element name. 30 | */ 31 | String value(); 32 | } 33 | -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/ResourcePath.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo; 16 | 17 | import java.lang.annotation.ElementType; 18 | import java.lang.annotation.Retention; 19 | import java.lang.annotation.RetentionPolicy; 20 | import java.lang.annotation.Target; 21 | 22 | /** 23 | * Associates a resource path with a service method. 24 | */ 25 | @Retention(RetentionPolicy.RUNTIME) 26 | @Target(ElementType.METHOD) 27 | public @interface ResourcePath { 28 | /** 29 | * The method's resource path. 30 | */ 31 | String value(); 32 | } 33 | -------------------------------------------------------------------------------- /kilo-test/src/test/java/org/httprpc/kilo/test/FilmServiceProxy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import org.httprpc.kilo.RequestMethod; 18 | import org.httprpc.kilo.ResourcePath; 19 | import org.httprpc.kilo.ServicePath; 20 | 21 | import java.io.IOException; 22 | import java.util.List; 23 | 24 | @ServicePath("films") 25 | public interface FilmServiceProxy { 26 | @RequestMethod("GET") 27 | List getFilms(String match) throws IOException; 28 | 29 | @RequestMethod("GET") 30 | @ResourcePath("?") 31 | FilmDetail getFilm(Integer filmID) throws IOException; 32 | } 33 | -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/Description.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo; 16 | 17 | import java.lang.annotation.ElementType; 18 | import java.lang.annotation.Retention; 19 | import java.lang.annotation.RetentionPolicy; 20 | import java.lang.annotation.Target; 21 | 22 | /** 23 | * Associates a description with an element. 24 | */ 25 | @Retention(RetentionPolicy.RUNTIME) 26 | @Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) 27 | public @interface Description { 28 | /** 29 | * The element description. 30 | */ 31 | String value(); 32 | } 33 | -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/sql/ForeignKey.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.sql; 16 | 17 | import java.lang.annotation.ElementType; 18 | import java.lang.annotation.Retention; 19 | import java.lang.annotation.RetentionPolicy; 20 | import java.lang.annotation.Target; 21 | 22 | /** 23 | * Indicates that a property represents a foreign key. 24 | */ 25 | @Retention(RetentionPolicy.RUNTIME) 26 | @Target(ElementType.METHOD) 27 | public @interface ForeignKey { 28 | /** 29 | * The type that defines the primary key to which the foreign key refers. 30 | */ 31 | Class value(); 32 | } 33 | -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/sql/Index.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.sql; 16 | 17 | import java.lang.annotation.ElementType; 18 | import java.lang.annotation.Retention; 19 | import java.lang.annotation.RetentionPolicy; 20 | import java.lang.annotation.Target; 21 | 22 | /** 23 | * Indicates that a property is part of the default sort order for an entity. 24 | */ 25 | @Retention(RetentionPolicy.RUNTIME) 26 | @Target(ElementType.METHOD) 27 | public @interface Index { 28 | /** 29 | * The relative order of the property within the index. 30 | */ 31 | int value() default 0; 32 | } 33 | -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/sql/Identifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.sql; 16 | 17 | import java.lang.annotation.ElementType; 18 | import java.lang.annotation.Retention; 19 | import java.lang.annotation.RetentionPolicy; 20 | import java.lang.annotation.Target; 21 | 22 | /** 23 | * Indicates that a property is part of the identifier for an entity. 24 | */ 25 | @Retention(RetentionPolicy.RUNTIME) 26 | @Target(ElementType.METHOD) 27 | public @interface Identifier { 28 | /** 29 | * The relative order of the property within the identifier. 30 | */ 31 | int value() default 0; 32 | } 33 | -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/sql/PrimaryKey.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.sql; 16 | 17 | import java.lang.annotation.ElementType; 18 | import java.lang.annotation.Retention; 19 | import java.lang.annotation.RetentionPolicy; 20 | import java.lang.annotation.Target; 21 | 22 | /** 23 | * Indicates that a property represents a primary key. 24 | */ 25 | @Retention(RetentionPolicy.RUNTIME) 26 | @Target(ElementType.METHOD) 27 | public @interface PrimaryKey { 28 | /** 29 | * Indicates that the key's values are automatically generated. 30 | */ 31 | boolean generated() default true; 32 | } 33 | -------------------------------------------------------------------------------- /kilo-test/src/main/java/org/httprpc/kilo/test/GreetingService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import jakarta.servlet.annotation.WebServlet; 18 | import org.httprpc.kilo.Description; 19 | import org.httprpc.kilo.RequestMethod; 20 | import org.httprpc.kilo.WebService; 21 | 22 | @WebServlet(urlPatterns = {"/*"}, loadOnStartup = 1) 23 | @Description("Greeting example service.") 24 | public class GreetingService extends WebService { 25 | @RequestMethod("GET") 26 | @Description("Returns a friendly greeting.") 27 | public String getGreeting() { 28 | return "Hello, World!"; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /kilo-client/src/test/java/org/httprpc/kilo/io/TextDecoderTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.io; 16 | 17 | import org.junit.jupiter.api.Test; 18 | 19 | import java.io.IOException; 20 | import java.io.StringReader; 21 | 22 | import static org.junit.jupiter.api.Assertions.*; 23 | 24 | public class TextDecoderTest { 25 | @Test 26 | public void testRead() throws IOException { 27 | var expected = "héllo/r/nwørld"; 28 | 29 | var textDecoder = new TextDecoder(); 30 | 31 | var actual = textDecoder.read(new StringReader(expected)); 32 | 33 | assertEquals(expected, actual); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /kilo-test/src/main/java/org/httprpc/kilo/test/Size.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import org.httprpc.kilo.Description; 18 | import org.httprpc.kilo.sql.Numeric; 19 | 20 | @Description("Represents a size option.") 21 | public enum Size implements Numeric { 22 | @Description("A small size.") SMALL(10), 23 | @Description("A medium size.") MEDIUM(20), 24 | @Description("A large size.") LARGE(30); 25 | 26 | private final int value; 27 | 28 | Size(int value) { 29 | this.value = value; 30 | } 31 | 32 | @Override 33 | public int value() { 34 | return value; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /kilo-test/src/test/java/org/httprpc/kilo/test/Vehicle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import org.httprpc.kilo.Required; 18 | 19 | public class Vehicle { 20 | private String manufacturer; 21 | private Integer year; 22 | 23 | @Required 24 | public String getManufacturer() { 25 | return manufacturer; 26 | } 27 | 28 | public void setManufacturer(String manufacturer) { 29 | this.manufacturer = manufacturer; 30 | } 31 | 32 | @Required 33 | public Integer getYear() { 34 | return year; 35 | } 36 | 37 | public void setYear(Integer year) { 38 | this.year = year; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /kilo-client/src/test/java/org/httprpc/kilo/sql/JSONTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.sql; 16 | 17 | import org.httprpc.kilo.Name; 18 | 19 | import java.util.List; 20 | import java.util.Map; 21 | 22 | @Table("json_test") 23 | public interface JSONTest { 24 | record Record( 25 | int a, 26 | int b, 27 | int c 28 | ) { 29 | } 30 | 31 | @Name("id") 32 | @Column("id") 33 | @PrimaryKey 34 | Integer getID(); 35 | 36 | @Column("list") 37 | @JSON 38 | List getList(); 39 | 40 | @Column("map") 41 | @JSON 42 | Map getMap(); 43 | 44 | @Column("record") 45 | @JSON 46 | Record getRecord(); 47 | } 48 | -------------------------------------------------------------------------------- /kilo-server/src/main/resources/org/httprpc/kilo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{$title}} 6 | 40 | 41 | 42 |
43 |

{{$services}}

44 |
    45 | 46 |
  1. 47 | {{contextPath}}{{path}}{{description}} 48 |
  2. 49 | 50 |
51 |
52 | 53 | -------------------------------------------------------------------------------- /kilo-test/src/main/java/org/httprpc/kilo/test/Rating.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import org.httprpc.kilo.Description; 18 | 19 | @Description("Represents a film rating.") 20 | public enum Rating { 21 | @Description("A \"G\" rating.") G("G"), 22 | @Description("A \"PG\" rating.") PG("PG"), 23 | @Description("A \"PG-13\" rating.") PG_13("PG-13"), 24 | @Description("An \"R\" rating.") R("R"), 25 | @Description("An \"NC-17\" rating.") NC_17("NC-17"); 26 | 27 | private final String value; 28 | 29 | Rating(String value) { 30 | this.value = value; 31 | } 32 | 33 | @Override 34 | public String toString() { 35 | return value; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /kilo-client/src/test/java/org/httprpc/kilo/sql/TemporalAccessorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.sql; 16 | 17 | import org.httprpc.kilo.Name; 18 | import org.httprpc.kilo.Required; 19 | 20 | import java.time.Instant; 21 | import java.time.LocalDate; 22 | import java.time.LocalTime; 23 | 24 | @Table("temporal_accessor_test") 25 | public interface TemporalAccessorTest { 26 | @Name("id") 27 | @Column("id") 28 | @PrimaryKey 29 | Integer getID(); 30 | 31 | @Column("local_date") 32 | @Required 33 | LocalDate getDate(); 34 | 35 | @Column("local_time") 36 | @Required 37 | LocalTime getTime(); 38 | 39 | @Column("instant") 40 | @Required 41 | Instant getInstant(); 42 | } 43 | -------------------------------------------------------------------------------- /kilo-test/src/main/java/org/httprpc/kilo/test/Category.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import org.httprpc.kilo.Description; 18 | import org.httprpc.kilo.Name; 19 | import org.httprpc.kilo.sql.Column; 20 | import org.httprpc.kilo.sql.Index; 21 | import org.httprpc.kilo.sql.PrimaryKey; 22 | import org.httprpc.kilo.sql.Table; 23 | 24 | @Table("category") 25 | @Description("Represents a film category.") 26 | public interface Category { 27 | @Name("id") 28 | @Column("category_id") 29 | @PrimaryKey 30 | @Description("The category's ID.") 31 | Integer getID(); 32 | 33 | @Column("name") 34 | @Index 35 | @Description("The name of the category.") 36 | String getName(); 37 | } 38 | -------------------------------------------------------------------------------- /kilo-test/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | plugins { 16 | id 'war' 17 | } 18 | 19 | dependencies { 20 | compileOnly "jakarta.servlet:jakarta.servlet-api:${servletAPIVersion}" 21 | 22 | implementation project(':kilo-server') 23 | 24 | implementation "org.mariadb.jdbc:mariadb-java-client:${connectorJVersion}" 25 | implementation 'org.hibernate.orm:hibernate-core:6.6.23.Final' 26 | 27 | testImplementation "org.junit.jupiter:junit-jupiter:${junitVersion}" 28 | 29 | testRuntimeOnly("org.junit.platform:junit-platform-launcher") 30 | } 31 | 32 | war { 33 | archiveFileName = "${project.name}.war" 34 | } 35 | 36 | tasks.withType(JavaCompile) { 37 | options.compilerArgs = [ 38 | '-parameters' 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /kilo-test/src/main/java/org/httprpc/kilo/test/Person.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import org.httprpc.kilo.Name; 18 | import org.httprpc.kilo.Required; 19 | 20 | public class Person { 21 | private String firstName = null; 22 | private String lastName = null; 23 | 24 | @Name("first_name") 25 | @Required 26 | public String getFirstName() { 27 | return firstName; 28 | } 29 | 30 | public void setFirstName(String firstName) { 31 | this.firstName = firstName; 32 | } 33 | 34 | @Name("last_name") 35 | @Required 36 | public String getLastName() { 37 | return lastName; 38 | } 39 | 40 | public void setLastName(String lastName) { 41 | this.lastName = lastName; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /kilo-test/src/test/java/org/httprpc/kilo/test/MathServiceProxy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import org.httprpc.kilo.RequestMethod; 18 | import org.httprpc.kilo.ResourcePath; 19 | import org.httprpc.kilo.ServicePath; 20 | 21 | import java.io.IOException; 22 | import java.util.List; 23 | 24 | @ServicePath("math") 25 | public interface MathServiceProxy { 26 | @RequestMethod("GET") 27 | @ResourcePath("sum") 28 | double getSum(double a, double b) throws IOException; 29 | 30 | @RequestMethod("GET") 31 | @ResourcePath("sum") 32 | double getSum(List values) throws IOException; 33 | 34 | default double getAverage(List values) throws IOException { 35 | return getSum(values) / values.size(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /kilo-test/src/test/java/org/httprpc/kilo/test/Course.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | public class Course { 18 | private String name; 19 | private String building; 20 | private int roomNumber; 21 | 22 | public String getName() { 23 | return name; 24 | } 25 | 26 | public void setName(String name) { 27 | this.name = name; 28 | } 29 | 30 | public String getBuilding() { 31 | return building; 32 | } 33 | 34 | public void setBuilding(String building) { 35 | this.building = building; 36 | } 37 | 38 | public int getRoomNumber() { 39 | return roomNumber; 40 | } 41 | 42 | public void setRoomNumber(int roomNumber) { 43 | this.roomNumber = roomNumber; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/io/TextDecoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.io; 16 | 17 | import java.io.IOException; 18 | import java.io.Reader; 19 | 20 | /** 21 | * Decodes plain text content. 22 | */ 23 | public class TextDecoder extends Decoder { 24 | @Override 25 | public String read(Reader reader) throws IOException { 26 | if (reader == null) { 27 | throw new IllegalArgumentException(); 28 | } 29 | 30 | reader = new BufferedReader(reader); 31 | 32 | var stringBuilder = new StringBuilder(1024); 33 | 34 | int c; 35 | while ((c = reader.read()) != EOF) { 36 | stringBuilder.append((char)c); 37 | } 38 | 39 | return stringBuilder.toString(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /kilo-test/src/main/java/org/httprpc/kilo/test/Pet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import org.httprpc.kilo.sql.Column; 18 | import org.httprpc.kilo.sql.ForeignKey; 19 | import org.httprpc.kilo.sql.Index; 20 | import org.httprpc.kilo.sql.PrimaryKey; 21 | import org.httprpc.kilo.sql.Table; 22 | 23 | import java.time.LocalDate; 24 | 25 | @Table("pet") 26 | public interface Pet { 27 | @Column("name") 28 | @PrimaryKey 29 | @Index 30 | String getName(); 31 | 32 | @Column("owner") 33 | @ForeignKey(Owner.class) 34 | String getOwner(); 35 | 36 | @Column("species") 37 | String getSpecies(); 38 | 39 | @Column("sex") 40 | String getSex(); 41 | 42 | @Column("birth") 43 | LocalDate getBirth(); 44 | 45 | @Column("death") 46 | LocalDate getDeath(); 47 | } 48 | -------------------------------------------------------------------------------- /kilo-test/src/test/resources/org/httprpc/kilo/test/breakfast-menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Belgian Waffles 6 | $5.95 7 | Two of our famous Belgian Waffles with plenty of real maple syrup 8 | 650 9 | 10 | 11 | 12 | Strawberry Belgian Waffles 13 | $7.95 14 | Light Belgian waffles covered with strawberries and whipped cream 15 | 900 16 | 17 | 18 | 19 | Berry-Berry Belgian Waffles 20 | $8.95 21 | Light Belgian waffles covered with an assortment of fresh berries and whipped cream 22 | 900 23 | 24 | 25 | 26 | French Toast 27 | $4.50 28 | Thick slices made from our homemade sourdough bread 29 | 600 30 | 31 | 32 | 33 | Homestyle Breakfast 34 | $6.95 35 | Two eggs, bacon or sausage, toast, and our ever-popular hash browns 36 | 950 37 | 38 | 39 | -------------------------------------------------------------------------------- /kilo-test/src/main/java/org/httprpc/kilo/test/Actor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import org.httprpc.kilo.Description; 18 | import org.httprpc.kilo.Name; 19 | import org.httprpc.kilo.sql.Column; 20 | import org.httprpc.kilo.sql.Index; 21 | import org.httprpc.kilo.sql.PrimaryKey; 22 | import org.httprpc.kilo.sql.Table; 23 | 24 | @Table("actor") 25 | @Description("Represents an actor.") 26 | public interface Actor { 27 | @Name("id") 28 | @Column("actor_id") 29 | @PrimaryKey 30 | @Description("The actor's ID.") 31 | Integer getID(); 32 | 33 | @Column("first_name") 34 | @Index(2) 35 | @Description("The actor's first name.") 36 | String getFirstName(); 37 | 38 | @Column("last_name") 39 | @Index(1) 40 | @Description("The actor's last name.") 41 | String getLastName(); 42 | } 43 | -------------------------------------------------------------------------------- /kilo-test/src/main/java/org/httprpc/kilo/test/MemberService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import jakarta.servlet.annotation.WebServlet; 18 | import org.httprpc.kilo.Name; 19 | import org.httprpc.kilo.RequestMethod; 20 | import org.httprpc.kilo.WebService; 21 | 22 | import java.util.List; 23 | 24 | import static org.httprpc.kilo.util.Collections.*; 25 | 26 | @WebServlet(urlPatterns = {"/members/*"}, loadOnStartup = 1) 27 | public class MemberService extends WebService { 28 | @RequestMethod("GET") 29 | public List getMembers( 30 | @Name("first_name") String firstName, 31 | @Name("last_name") String lastName 32 | ) { 33 | var member = new Person(); 34 | 35 | member.setFirstName(firstName); 36 | member.setLastName(lastName); 37 | 38 | return listOf(member); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /kilo-test/src/main/java/org/httprpc/kilo/test/Employee.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import org.httprpc.kilo.Required; 18 | import org.httprpc.kilo.sql.Column; 19 | import org.httprpc.kilo.sql.PrimaryKey; 20 | import org.httprpc.kilo.sql.Table; 21 | 22 | import java.time.LocalDate; 23 | 24 | @Table("employees") 25 | public interface Employee { 26 | @Column("emp_no") 27 | @PrimaryKey 28 | Integer getEmployeeNumber(); 29 | 30 | @Column("first_name") 31 | @Required 32 | String getFirstName(); 33 | 34 | @Column("last_name") 35 | @Required 36 | String getLastName(); 37 | 38 | @Column("gender") 39 | @Required 40 | String getGender(); 41 | 42 | @Column("birth_date") 43 | @Required 44 | LocalDate getBirthDate(); 45 | 46 | @Column("hire_date") 47 | @Required 48 | LocalDate getHireDate(); 49 | } 50 | -------------------------------------------------------------------------------- /kilo-test/src/main/java/org/httprpc/kilo/test/FilmDetail.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import org.httprpc.kilo.Description; 18 | import org.httprpc.kilo.sql.Column; 19 | 20 | import java.util.List; 21 | 22 | @Description("Represents detailed information about a film.") 23 | public interface FilmDetail extends Film { 24 | @Column("description") 25 | @Description("A description of the film.") 26 | String getDescription(); 27 | 28 | @Column("length") 29 | @Description("The length of the film, in minutes.") 30 | Integer getLength(); 31 | 32 | @Description("The actors that appeared in the film.") 33 | List getActors(); 34 | void setActors(List actors); 35 | 36 | @Description("The categories associated with the film.") 37 | List getCategories(); 38 | void setCategories(List categories); 39 | } 40 | -------------------------------------------------------------------------------- /kilo-client/src/test/java/org/httprpc/kilo/io/TextEncoderTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.io; 16 | 17 | import org.junit.jupiter.api.Test; 18 | 19 | import java.io.IOException; 20 | import java.io.StringWriter; 21 | import java.time.LocalDate; 22 | 23 | import static org.junit.jupiter.api.Assertions.*; 24 | 25 | public class TextEncoderTest { 26 | @Test 27 | public void testString() throws IOException { 28 | assertEquals("héllo/r/nwørld", encode("héllo/r/nwørld")); 29 | } 30 | 31 | @Test 32 | public void testLocalDate() throws IOException { 33 | assertEquals("2024-03-31", encode(LocalDate.of(2024, 3, 31))); 34 | } 35 | 36 | private static String encode(Object value) throws IOException { 37 | var textEncoder = new TextEncoder(); 38 | 39 | var writer = new StringWriter(); 40 | 41 | textEncoder.write(value, writer); 42 | 43 | return writer.toString(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /kilo-test/src/main/java/org/httprpc/kilo/test/ItemDetail.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import org.httprpc.kilo.Description; 18 | import org.httprpc.kilo.sql.Column; 19 | import org.httprpc.kilo.sql.Final; 20 | 21 | import java.util.Date; 22 | 23 | @Description("Represents detailed information about an item in the catalog.") 24 | public interface ItemDetail extends Item { 25 | @Column("size") 26 | @Description("The item's size.") 27 | Size getSize(); 28 | void setSize(Size size); 29 | 30 | @Column("color") 31 | @Description("The item's color.") 32 | String getColor(); 33 | void setColor(String color); 34 | 35 | @Column("weight") 36 | @Description("The item's weight.") 37 | Double getWeight(); 38 | void setWeight(Double weight); 39 | 40 | @Column("created") 41 | @Final 42 | @Description("The date the item was created.") 43 | Date getCreated(); 44 | void setCreated(Date created); 45 | } 46 | -------------------------------------------------------------------------------- /kilo-test/src/test/java/org/httprpc/kilo/test/FilmsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import org.httprpc.kilo.WebServiceProxy; 18 | import org.junit.jupiter.api.Test; 19 | 20 | import java.io.IOException; 21 | import java.net.URI; 22 | 23 | import static org.junit.jupiter.api.Assertions.*; 24 | 25 | public class FilmsTest { 26 | private static final URI baseURI = URI.create("http://localhost:8080/kilo-test/"); 27 | 28 | @Test 29 | public void testFilms() throws IOException { 30 | var filmServiceProxy = WebServiceProxy.of(FilmServiceProxy.class, baseURI); 31 | 32 | var films = filmServiceProxy.getFilms("n*"); 33 | 34 | assertFalse(films.isEmpty()); 35 | 36 | var film = filmServiceProxy.getFilm(films.getFirst().getID()); 37 | 38 | assertEquals('n', Character.toLowerCase(film.getTitle().charAt(0))); 39 | 40 | assertFalse(film.getActors().isEmpty()); 41 | assertFalse(film.getCategories().isEmpty()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /kilo-test/src/main/java/org/httprpc/kilo/test/Item.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import org.httprpc.kilo.Description; 18 | import org.httprpc.kilo.Name; 19 | import org.httprpc.kilo.Required; 20 | import org.httprpc.kilo.sql.Column; 21 | import org.httprpc.kilo.sql.Index; 22 | import org.httprpc.kilo.sql.PrimaryKey; 23 | import org.httprpc.kilo.sql.Table; 24 | 25 | @Table("item") 26 | @Description("Represents an item in the catalog.") 27 | public interface Item { 28 | @Name("id") 29 | @Column("id") 30 | @PrimaryKey 31 | @Description("The item's ID.") 32 | Integer getID(); 33 | void setID(Integer id); 34 | 35 | @Column("description") 36 | @Index 37 | @Description("The item's description.") 38 | @Required 39 | String getDescription(); 40 | void setDescription(String description); 41 | 42 | @Column("price") 43 | @Description("The item's price.") 44 | @Required 45 | Double getPrice(); 46 | void setPrice(Double price); 47 | } 48 | -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/WebServiceException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo; 16 | 17 | import java.io.IOException; 18 | 19 | /** 20 | * Thrown to indicate that a service operation returned an error. 21 | */ 22 | public class WebServiceException extends IOException { 23 | private int statusCode; 24 | 25 | /** 26 | * Constructs a new web service exception. 27 | * 28 | * @param message 29 | * The error message returned by the service. 30 | * 31 | * @param statusCode 32 | * The HTTP status code returned by the service. 33 | */ 34 | public WebServiceException(String message, int statusCode) { 35 | super(message); 36 | 37 | this.statusCode = statusCode; 38 | } 39 | 40 | /** 41 | * Returns the HTTP status code returned by the service. 42 | * 43 | * @return 44 | * The HTTP status code returned by the service. 45 | */ 46 | public int getStatusCode() { 47 | return statusCode; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /kilo-test/src/main/java/org/httprpc/kilo/test/Film.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import org.httprpc.kilo.Description; 18 | import org.httprpc.kilo.Name; 19 | import org.httprpc.kilo.Required; 20 | import org.httprpc.kilo.sql.Column; 21 | import org.httprpc.kilo.sql.Index; 22 | import org.httprpc.kilo.sql.PrimaryKey; 23 | import org.httprpc.kilo.sql.Table; 24 | 25 | import java.time.LocalDate; 26 | 27 | @Table("film") 28 | @Description("Represents a film.") 29 | public interface Film { 30 | @Name("id") 31 | @Column("film_id") 32 | @PrimaryKey 33 | @Description("The film's ID.") 34 | Integer getID(); 35 | 36 | @Column("title") 37 | @Index 38 | @Description("The title of the film.") 39 | @Required 40 | String getTitle(); 41 | 42 | @Column("rating") 43 | @Description("The film's rating.") 44 | Rating getRating(); 45 | 46 | @Column("release_year") 47 | @Description("The date the film was released.") 48 | LocalDate getReleaseDate(); 49 | } 50 | -------------------------------------------------------------------------------- /kilo-test/src/test/java/org/httprpc/kilo/test/CatalogServiceProxy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import org.httprpc.kilo.RequestMethod; 18 | import org.httprpc.kilo.ResourcePath; 19 | import org.httprpc.kilo.ServicePath; 20 | 21 | import java.io.IOException; 22 | import java.util.List; 23 | 24 | @ServicePath("catalog") 25 | public interface CatalogServiceProxy { 26 | @RequestMethod("GET") 27 | @ResourcePath("items") 28 | List getItems() throws IOException; 29 | 30 | @RequestMethod("GET") 31 | @ResourcePath("items/?") 32 | ItemDetail getItem(Integer itemID) throws IOException; 33 | 34 | @RequestMethod("POST") 35 | @ResourcePath("items") 36 | ItemDetail addItem(ItemDetail item) throws IOException; 37 | 38 | @RequestMethod("PUT") 39 | @ResourcePath("items/?") 40 | ItemDetail updateItem(Integer itemID, ItemDetail item) throws IOException; 41 | 42 | @RequestMethod("DELETE") 43 | @ResourcePath("items/?") 44 | void deleteItem(Integer itemID) throws IOException; 45 | } 46 | -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/io/TextEncoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.io; 16 | 17 | import java.io.IOException; 18 | import java.io.Writer; 19 | 20 | /** 21 | * Encodes plain text content. 22 | */ 23 | public class TextEncoder extends Encoder { 24 | @Override 25 | public void write(Object value, Writer writer) throws IOException { 26 | if (value == null || writer == null) { 27 | throw new IllegalArgumentException(); 28 | } 29 | 30 | writer = new BufferedWriter(writer); 31 | 32 | try { 33 | if (value instanceof CharSequence text) { 34 | encode(text, writer); 35 | } else { 36 | encode(value.toString(), writer); 37 | } 38 | } finally { 39 | writer.flush(); 40 | } 41 | } 42 | 43 | private void encode(CharSequence text, Writer writer) throws IOException { 44 | for (int i = 0, n = text.length(); i < n; i++) { 45 | writer.write(text.charAt(i)); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/io/BufferedReader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.io; 16 | 17 | import java.io.IOException; 18 | import java.io.Reader; 19 | 20 | class BufferedReader extends Reader { 21 | private Reader reader; 22 | private char[] buffer; 23 | 24 | private int i = 0; 25 | private int n = 0; 26 | 27 | BufferedReader(Reader reader) { 28 | this.reader = reader; 29 | 30 | buffer = new char[8192]; 31 | } 32 | 33 | @Override 34 | public int read() throws IOException { 35 | if (i == n) { 36 | i = 0; 37 | 38 | n = reader.read(buffer); 39 | 40 | if (n == -1) { 41 | n = 0; 42 | 43 | return -1; 44 | } 45 | } 46 | 47 | return buffer[i++]; 48 | } 49 | 50 | @Override 51 | public int read(char[] cbuf, int off, int len) { 52 | throw new UnsupportedOperationException(); 53 | } 54 | 55 | @Override 56 | public void close() { 57 | throw new UnsupportedOperationException(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /kilo-test/src/test/java/org/httprpc/kilo/test/EmployeesTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import org.httprpc.kilo.WebServiceProxy; 18 | 19 | import java.io.IOException; 20 | import java.net.URI; 21 | import java.util.List; 22 | 23 | public class EmployeesTest { 24 | public static void main(String[] args) throws IOException { 25 | var baseURI = URI.create("http://localhost:8080/kilo-test/"); 26 | 27 | logTiming(baseURI, "employees"); 28 | logTiming(baseURI, "employees/stream"); 29 | logTiming(baseURI, "employees/hibernate"); 30 | logTiming(baseURI, "employees/hibernate-stream"); 31 | } 32 | 33 | private static void logTiming(URI baseURI, String path) throws IOException { 34 | var webServiceProxy = new WebServiceProxy("GET", baseURI.resolve(path)); 35 | 36 | var t0 = System.currentTimeMillis(); 37 | 38 | var result = (List)webServiceProxy.invoke(); 39 | 40 | var t1 = System.currentTimeMillis(); 41 | 42 | System.out.println(String.format("Retrieved %d rows from %s in %.1fs", result.size(), path, (t1 - t0) / 1000.0)); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /kilo-test/src/main/java/org/httprpc/kilo/test/MathService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import jakarta.servlet.annotation.WebServlet; 18 | import org.httprpc.kilo.Description; 19 | import org.httprpc.kilo.RequestMethod; 20 | import org.httprpc.kilo.ResourcePath; 21 | import org.httprpc.kilo.WebService; 22 | 23 | import java.util.List; 24 | 25 | @WebServlet(urlPatterns = {"/math/*"}, loadOnStartup = 1) 26 | @Description("Math example service.") 27 | public class MathService extends WebService { 28 | @RequestMethod("GET") 29 | @ResourcePath("sum") 30 | @Description("Calculates the sum of two numbers.") 31 | public double getSum( 32 | @Description("The first number.") double a, 33 | @Description("The second number.") double b 34 | ) { 35 | return a + b; 36 | } 37 | 38 | @RequestMethod("GET") 39 | @ResourcePath("sum") 40 | @Description("Calculates the sum of a list of numbers.") 41 | public double getSum( 42 | @Description("The numbers to add.") List values 43 | ) { 44 | var total = 0.0; 45 | 46 | for (var value : values) { 47 | total += value; 48 | } 49 | 50 | return total; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /kilo-test/src/test/java/org/httprpc/kilo/test/DocumentationTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import org.httprpc.kilo.WebServiceProxy; 18 | import org.httprpc.kilo.io.JSONDecoder; 19 | import org.junit.jupiter.api.Test; 20 | 21 | import java.io.IOException; 22 | import java.net.URI; 23 | import java.util.Map; 24 | 25 | import static org.httprpc.kilo.util.Collections.*; 26 | import static org.junit.jupiter.api.Assertions.*; 27 | 28 | public class DocumentationTest { 29 | private static final URI baseURI = URI.create("http://localhost:8080/kilo-test/"); 30 | 31 | @Test 32 | public void testDocumentation() throws IOException { 33 | testDocumentation("math"); 34 | testDocumentation("catalog"); 35 | } 36 | 37 | private void testDocumentation(String name) throws IOException { 38 | Map expected; 39 | try (var inputStream = getClass().getResourceAsStream(String.format("%s.json", name))) { 40 | var jsonDecoder = new JSONDecoder(); 41 | 42 | expected = (Map)jsonDecoder.read(inputStream); 43 | } 44 | 45 | var webServiceProxy = new WebServiceProxy("GET", baseURI.resolve(String.format("%s?api", name))); 46 | 47 | webServiceProxy.setHeaders(mapOf( 48 | entry("Accept", "application/json") 49 | )); 50 | 51 | var actual = webServiceProxy.invoke(); 52 | 53 | assertEquals(expected, actual); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /kilo-client/src/test/java/org/httprpc/kilo/util/OptionalsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.util; 16 | 17 | import org.junit.jupiter.api.Test; 18 | 19 | import java.util.Optional; 20 | 21 | import static org.httprpc.kilo.util.Optionals.*; 22 | import static org.junit.jupiter.api.Assertions.*; 23 | 24 | public class OptionalsTest { 25 | @Test 26 | public void testCoalesce() { 27 | var value = 123; 28 | 29 | var a = Optional.ofNullable(null).orElse(value); // 123 30 | var b = coalesce(null, () -> value); // 123 31 | 32 | assertEquals(a, b); 33 | } 34 | 35 | @Test 36 | public void testMap() { 37 | var value = "hello"; 38 | 39 | var a = Optional.ofNullable(value).map(String::length).orElse(null); // 5 40 | var b = map(value, String::length); // 5 41 | 42 | assertEquals(a, b); 43 | 44 | assertNull(map(null, String::length)); 45 | } 46 | 47 | @Test 48 | public void testPerform() { 49 | var stringBuilder = new StringBuilder(); 50 | 51 | Optional.ofNullable("abc").ifPresent(stringBuilder::append); // abc 52 | perform("def", stringBuilder::append); // abcdef 53 | 54 | perform(null, stringBuilder::append); 55 | 56 | assertEquals("abcdef", stringBuilder.toString()); 57 | } 58 | 59 | @Test 60 | public void testCast() { 61 | var text = cast("abc", String.class); // abc 62 | 63 | assertNotNull(text); 64 | 65 | var number = cast("abc", Double.class); // null 66 | 67 | assertNull(number); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/io/BufferedWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.io; 16 | 17 | import java.io.IOException; 18 | import java.io.Writer; 19 | 20 | class BufferedWriter extends Writer { 21 | private Writer writer; 22 | private char[] buffer; 23 | 24 | private int i = 0; 25 | 26 | BufferedWriter(Writer writer) { 27 | this.writer = writer; 28 | 29 | buffer = new char[8192]; 30 | } 31 | 32 | @Override 33 | public void write(int c) throws IOException { 34 | buffer[i++] = (char)c; 35 | 36 | if (i == buffer.length) { 37 | flush(); 38 | } 39 | } 40 | 41 | @Override 42 | public void write(char[] cbuf, int off, int len) { 43 | throw new UnsupportedOperationException(); 44 | } 45 | 46 | @Override 47 | public void write(String str) throws IOException { 48 | var n = str.length(); 49 | var j = 0; 50 | 51 | while (j < n) { 52 | var c = Math.min(n - j, buffer.length - i); 53 | var k = j + c; 54 | 55 | str.getChars(j, k, buffer, i); 56 | 57 | j = k; 58 | 59 | i += c; 60 | 61 | if (i == buffer.length) { 62 | flush(); 63 | } 64 | } 65 | } 66 | 67 | @Override 68 | public void write(String str, int off, int len) { 69 | throw new UnsupportedOperationException(); 70 | } 71 | 72 | @Override 73 | public void flush() throws IOException { 74 | writer.write(buffer, 0, i); 75 | writer.flush(); 76 | 77 | i = 0; 78 | } 79 | 80 | @Override 81 | public void close() { 82 | throw new UnsupportedOperationException(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /kilo-server/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | plugins { 16 | id 'java-library' 17 | id 'maven-publish' 18 | id 'signing' 19 | } 20 | 21 | java { 22 | sourceCompatibility = JavaVersion.VERSION_21 23 | targetCompatibility = JavaVersion.VERSION_21 24 | 25 | withJavadocJar() 26 | withSourcesJar() 27 | } 28 | 29 | dependencies { 30 | compileOnly "jakarta.servlet:jakarta.servlet-api:${servletAPIVersion}" 31 | 32 | api project(':kilo-client') 33 | } 34 | 35 | publishing { 36 | repositories { 37 | maven { 38 | credentials { 39 | username = project.properties['centralPortalUsername'] ?: '' 40 | password = project.properties['centralPortalPassword'] ?: '' 41 | } 42 | } 43 | } 44 | 45 | publications { 46 | maven(MavenPublication) { 47 | artifactId = project.name 48 | 49 | from components.java 50 | 51 | pom { 52 | name = project.name 53 | description = "${projectDescription}" 54 | url = "${projectURL}" 55 | licenses { 56 | license { 57 | name = "${licenseName}" 58 | url = "${licenseURL}" 59 | } 60 | } 61 | developers { 62 | developer { 63 | name = "${developerName}" 64 | email = "${developerEmail}" 65 | } 66 | } 67 | scm { 68 | connection = "${scmConnection}" 69 | url = "${scmURL}" 70 | } 71 | } 72 | } 73 | } 74 | } 75 | 76 | signing { 77 | sign publishing.publications 78 | } 79 | -------------------------------------------------------------------------------- /kilo-client/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | plugins { 16 | id 'java-library' 17 | id 'maven-publish' 18 | id 'signing' 19 | } 20 | 21 | java { 22 | sourceCompatibility = JavaVersion.VERSION_21 23 | targetCompatibility = JavaVersion.VERSION_21 24 | 25 | withJavadocJar() 26 | withSourcesJar() 27 | } 28 | 29 | dependencies { 30 | testImplementation "org.junit.jupiter:junit-jupiter:${junitVersion}" 31 | testImplementation "org.mariadb.jdbc:mariadb-java-client:${connectorJVersion}" 32 | 33 | testRuntimeOnly("org.junit.platform:junit-platform-launcher") 34 | } 35 | 36 | publishing { 37 | repositories { 38 | maven { 39 | credentials { 40 | username = project.properties['centralPortalUsername'] ?: '' 41 | password = project.properties['centralPortalPassword'] ?: '' 42 | } 43 | } 44 | } 45 | 46 | publications { 47 | maven(MavenPublication) { 48 | artifactId = project.name 49 | 50 | from components.java 51 | 52 | pom { 53 | name = project.name 54 | description = "${projectDescription}" 55 | url = "${projectURL}" 56 | licenses { 57 | license { 58 | name = "${licenseName}" 59 | url = "${licenseURL}" 60 | } 61 | } 62 | developers { 63 | developer { 64 | name = "${developerName}" 65 | email = "${developerEmail}" 66 | } 67 | } 68 | scm { 69 | connection = "${scmConnection}" 70 | url = "${scmURL}" 71 | } 72 | } 73 | } 74 | } 75 | } 76 | 77 | signing { 78 | sign publishing.publications 79 | } 80 | -------------------------------------------------------------------------------- /kilo-client/src/test/java/org/httprpc/kilo/beans/TestInterface.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.beans; 16 | 17 | import org.httprpc.kilo.Name; 18 | import org.httprpc.kilo.Required; 19 | 20 | import java.math.BigInteger; 21 | import java.net.URI; 22 | import java.nio.file.Path; 23 | import java.time.DayOfWeek; 24 | import java.time.Duration; 25 | import java.time.Instant; 26 | import java.time.LocalDate; 27 | import java.time.LocalDateTime; 28 | import java.time.LocalTime; 29 | import java.time.Period; 30 | import java.util.Date; 31 | import java.util.List; 32 | import java.util.Map; 33 | import java.util.UUID; 34 | 35 | public interface TestInterface { 36 | interface NestedInterface { 37 | @Required 38 | Boolean getFlag(); 39 | void setFlag(Boolean flag); 40 | 41 | Character getCharacter(); 42 | } 43 | 44 | @Name("i") 45 | int getInteger(); 46 | void setInteger(int i); 47 | 48 | @Required 49 | long getLong(); 50 | double getDouble(); 51 | 52 | @Required 53 | String getString(); 54 | void setString(String string); 55 | 56 | BigInteger getBigInteger(); 57 | DayOfWeek getDayOfWeek(); 58 | Date getDate(); 59 | Instant getInstant(); 60 | LocalDate getLocalDate(); 61 | LocalTime getLocalTime(); 62 | LocalDateTime getLocalDateTime(); 63 | Duration getDuration(); 64 | Period getPeriod(); 65 | UUID getUUID(); 66 | URI getURI(); 67 | Path getPath(); 68 | 69 | NestedInterface getNestedBean(); 70 | void setNestedBean(NestedInterface nestedBean); 71 | 72 | @Required 73 | List getIntegerList(); 74 | void setIntegerList(List integerList); 75 | 76 | List getNestedBeanList(); 77 | 78 | Map getDoubleMap(); 79 | Map getNestedBeanMap(); 80 | 81 | TestRecord getTestRecord(); 82 | List getTestRecordList(); 83 | } 84 | -------------------------------------------------------------------------------- /kilo-test/src/main/java/org/httprpc/kilo/test/BulkUploadService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import jakarta.servlet.annotation.WebServlet; 18 | import org.httprpc.kilo.RequestMethod; 19 | import org.httprpc.kilo.ResourcePath; 20 | import org.httprpc.kilo.beans.BeanAdapter; 21 | import org.httprpc.kilo.sql.QueryBuilder; 22 | 23 | import java.sql.SQLException; 24 | import java.util.List; 25 | 26 | @WebServlet(urlPatterns = {"/bulk-upload/*"}, loadOnStartup = 1) 27 | public class BulkUploadService extends AbstractDatabaseService { 28 | private static final int BATCH_SIZE = 25000; 29 | 30 | @RequestMethod("POST") 31 | @ResourcePath("upload") 32 | public void upload(List rows) throws SQLException { 33 | var queryBuilder = new QueryBuilder(); 34 | 35 | queryBuilder.appendLine("insert into bulk_upload_test (text1, text2, number1, number2, number3)"); 36 | queryBuilder.appendLine("values (:text1, :text2, :number1, :number2, :number3)"); 37 | 38 | try (var statement = queryBuilder.prepare(getConnection())) { 39 | for (var row : rows) { 40 | queryBuilder.executeUpdate(statement, new BeanAdapter(row)); 41 | } 42 | } 43 | } 44 | 45 | @RequestMethod("POST") 46 | @ResourcePath("upload-batch") 47 | public void uploadBatch(List rows) throws SQLException { 48 | var queryBuilder = new QueryBuilder(); 49 | 50 | queryBuilder.appendLine("insert into bulk_upload_test (text1, text2, number1, number2, number3)"); 51 | queryBuilder.appendLine("values (:text1, :text2, :number1, :number2, :number3)"); 52 | 53 | try (var statement = queryBuilder.prepare(getConnection())) { 54 | var i = 0; 55 | 56 | for (var row : rows) { 57 | queryBuilder.addBatch(statement, new BeanAdapter(row)); 58 | 59 | if (++i % BATCH_SIZE == 0) { 60 | statement.executeBatch(); 61 | } 62 | } 63 | 64 | statement.executeBatch(); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /kilo-test/src/test/java/org/httprpc/kilo/test/BulkUploadTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import org.httprpc.kilo.WebServiceProxy; 18 | 19 | import java.io.IOException; 20 | import java.net.URI; 21 | import java.util.Iterator; 22 | 23 | public class BulkUploadTest { 24 | public static class Rows implements Iterable { 25 | private int count; 26 | 27 | private int i = 0; 28 | 29 | public Rows(int count) { 30 | this.count = count; 31 | } 32 | 33 | private Row row = new Row("abcdefghijklmnopqrstuvwxyz", "ABCDEFG", 123456, 101.05, 2002.0125); 34 | 35 | @Override 36 | public Iterator iterator() { 37 | return new Iterator<>() { 38 | @Override 39 | public boolean hasNext() { 40 | return i < count; 41 | } 42 | 43 | @Override 44 | public Row next() { 45 | i++; 46 | 47 | return row; 48 | } 49 | }; 50 | } 51 | } 52 | 53 | public static void main(String[] args) throws IOException { 54 | var baseURI = URI.create("http://localhost:8080/kilo-test/bulk-upload/"); 55 | 56 | var t0 = System.currentTimeMillis(); 57 | 58 | logTiming(baseURI, "upload", 25000, t0); 59 | 60 | var t1 = System.currentTimeMillis(); 61 | 62 | logTiming(baseURI, "upload-batch", 500000, t1); 63 | } 64 | 65 | private static void logTiming(URI baseURI, String path, int count, long start) throws IOException { 66 | var webServiceProxy = new WebServiceProxy("POST", baseURI.resolve(path)); 67 | 68 | webServiceProxy.setBody(new Rows(count)); 69 | 70 | webServiceProxy.setReadTimeout(120000); 71 | webServiceProxy.setChunkSize(65536); 72 | 73 | webServiceProxy.invoke(); 74 | 75 | var current = System.currentTimeMillis(); 76 | 77 | System.out.println(String.format("Uploaded %d rows in %.1fs", count, (current - start) / 1000.0)); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/io/Encoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.io; 16 | 17 | import java.io.IOException; 18 | import java.io.OutputStream; 19 | import java.io.OutputStreamWriter; 20 | import java.io.Writer; 21 | import java.nio.charset.Charset; 22 | import java.nio.charset.StandardCharsets; 23 | 24 | /** 25 | * Abstract base class for encoders. 26 | * 27 | * @param 28 | * The type of value consumed by the encoder. 29 | */ 30 | public abstract class Encoder { 31 | private Charset charset = StandardCharsets.UTF_8; 32 | 33 | /** 34 | * Returns the character set to use when encoding to an output stream. 35 | * 36 | * @return 37 | * The output stream's character set. 38 | */ 39 | public Charset getCharset() { 40 | return charset; 41 | } 42 | 43 | /** 44 | * Sets the character set to use when encoding to an output stream. 45 | * 46 | * @param charset 47 | * The output stream's character set. 48 | */ 49 | public void setCharset(Charset charset) { 50 | if (charset == null) { 51 | throw new IllegalArgumentException(); 52 | } 53 | 54 | this.charset = charset; 55 | } 56 | 57 | /** 58 | * Writes a value to an output stream. 59 | * 60 | * @param value 61 | * The value to encode. 62 | * 63 | * @param outputStream 64 | * The output stream to write to. 65 | * 66 | * @throws IOException 67 | * If an exception occurs. 68 | */ 69 | public void write(T value, OutputStream outputStream) throws IOException { 70 | if (outputStream == null) { 71 | throw new IllegalArgumentException(); 72 | } 73 | 74 | write(value, new OutputStreamWriter(outputStream, charset)); 75 | } 76 | 77 | /** 78 | * Writes a value to a character stream. 79 | * 80 | * @param value 81 | * The value to encode. 82 | * 83 | * @param writer 84 | * The character stream to write to. 85 | * 86 | * @throws IOException 87 | * If an exception occurs. 88 | */ 89 | public abstract void write(T value, Writer writer) throws IOException; 90 | } 91 | -------------------------------------------------------------------------------- /kilo-server/src/main/java/org/httprpc/kilo/IndexServlet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo; 16 | 17 | import jakarta.servlet.annotation.WebServlet; 18 | import jakarta.servlet.http.HttpServlet; 19 | import jakarta.servlet.http.HttpServletRequest; 20 | import jakarta.servlet.http.HttpServletResponse; 21 | import org.httprpc.kilo.io.JSONEncoder; 22 | import org.httprpc.kilo.io.TemplateEncoder; 23 | 24 | import java.io.IOException; 25 | import java.nio.charset.StandardCharsets; 26 | import java.util.ResourceBundle; 27 | 28 | import static org.httprpc.kilo.util.Collections.*; 29 | 30 | /** 31 | * Generates an index of all active services. 32 | */ 33 | @WebServlet(urlPatterns = {""}, loadOnStartup = Integer.MAX_VALUE) 34 | public class IndexServlet extends HttpServlet { 35 | @Override 36 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { 37 | var serviceDescriptors = WebService.getServiceDescriptors(); 38 | 39 | var accept = request.getHeader("Accept"); 40 | 41 | if (accept != null && accept.equalsIgnoreCase(WebService.APPLICATION_JSON)) { 42 | response.setContentType(String.format(WebService.CONTENT_TYPE_FORMAT, WebService.APPLICATION_JSON, StandardCharsets.UTF_8)); 43 | 44 | var jsonEncoder = new JSONEncoder(); 45 | 46 | jsonEncoder.write(serviceDescriptors, response.getOutputStream()); 47 | } else { 48 | response.setContentType(String.format(WebService.CONTENT_TYPE_FORMAT, WebService.TEXT_HTML, StandardCharsets.UTF_8)); 49 | 50 | var templateEncoder = new TemplateEncoder(IndexServlet.class, "index.html"); 51 | 52 | var locale = request.getLocale(); 53 | 54 | templateEncoder.setResourceBundle(ResourceBundle.getBundle(IndexServlet.class.getName(), locale)); 55 | templateEncoder.setLocale(locale); 56 | 57 | templateEncoder.write(mapOf( 58 | entry("language", locale.getLanguage()), 59 | entry("contextPath", request.getContextPath()), 60 | entry("services", serviceDescriptors) 61 | ), response.getOutputStream()); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /kilo-test/src/main/java/org/httprpc/kilo/test/HibernateEmployee.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import jakarta.persistence.Column; 18 | import jakarta.persistence.Entity; 19 | import jakarta.persistence.Id; 20 | import jakarta.persistence.Table; 21 | 22 | import java.time.LocalDate; 23 | 24 | @Entity 25 | @Table(name="employees") 26 | public class HibernateEmployee implements Employee { 27 | private Integer employeeNumber; 28 | private String firstName; 29 | private String lastName; 30 | private String gender; 31 | private LocalDate birthDate; 32 | private LocalDate hireDate; 33 | 34 | @Id 35 | @Column(name = "emp_no") 36 | @Override 37 | public Integer getEmployeeNumber() { 38 | return employeeNumber; 39 | } 40 | 41 | public void setEmployeeNumber(Integer employeeNumber) { 42 | this.employeeNumber = employeeNumber; 43 | } 44 | 45 | @Column(name = "first_name") 46 | @Override 47 | public String getFirstName() { 48 | return firstName; 49 | } 50 | 51 | public void setFirstName(String firstName) { 52 | this.firstName = firstName; 53 | } 54 | 55 | @Column(name = "last_name") 56 | @Override 57 | public String getLastName() { 58 | return lastName; 59 | } 60 | 61 | public void setLastName(String lastName) { 62 | this.lastName = lastName; 63 | } 64 | 65 | @Column(name = "gender") 66 | @Override 67 | public String getGender() { 68 | return gender; 69 | } 70 | 71 | public void setGender(String gender) { 72 | this.gender = gender; 73 | } 74 | 75 | @Column(name = "birth_date", columnDefinition = "DATE") 76 | @Override 77 | public LocalDate getBirthDate() { 78 | return birthDate; 79 | } 80 | 81 | public void setBirthDate(LocalDate birthDate) { 82 | this.birthDate = birthDate; 83 | } 84 | 85 | @Column(name = "hire_date", columnDefinition = "DATE") 86 | @Override 87 | public LocalDate getHireDate() { 88 | return hireDate; 89 | } 90 | 91 | public void setHireDate(LocalDate hireDate) { 92 | this.hireDate = hireDate; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/io/Decoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.io; 16 | 17 | import java.io.IOException; 18 | import java.io.InputStream; 19 | import java.io.InputStreamReader; 20 | import java.io.Reader; 21 | import java.nio.charset.Charset; 22 | import java.nio.charset.StandardCharsets; 23 | 24 | /** 25 | * Abstract base class for decoders. 26 | * 27 | * @param 28 | * The type of value produced by the decoder. 29 | */ 30 | public abstract class Decoder { 31 | private Charset charset = StandardCharsets.UTF_8; 32 | 33 | /** 34 | * Represents an "end of file" character. 35 | */ 36 | protected static final int EOF = -1; 37 | 38 | /** 39 | * Returns the character set to use when decoding from an input stream. 40 | * 41 | * @return 42 | * The input stream's character set. 43 | */ 44 | public Charset getCharset() { 45 | return charset; 46 | } 47 | 48 | /** 49 | * Sets the character set to use when decoding from an input stream. 50 | * 51 | * @param charset 52 | * The input stream's character set. 53 | */ 54 | public void setCharset(Charset charset) { 55 | if (charset == null) { 56 | throw new IllegalArgumentException(); 57 | } 58 | 59 | this.charset = charset; 60 | } 61 | 62 | /** 63 | * Reads a value from an input stream. 64 | * 65 | * @param inputStream 66 | * The input stream to read from. 67 | * 68 | * @return 69 | * The decoded value. 70 | * 71 | * @throws IOException 72 | * If an exception occurs. 73 | */ 74 | public T read(InputStream inputStream) throws IOException { 75 | if (inputStream == null) { 76 | throw new IllegalArgumentException(); 77 | } 78 | 79 | return read(new InputStreamReader(inputStream, charset)); 80 | } 81 | 82 | /** 83 | * Reads a value from a character stream. 84 | * 85 | * @param reader 86 | * The character stream to read from. 87 | * 88 | * @return 89 | * The decoded value. 90 | * 91 | * @throws IOException 92 | * If an exception occurs. 93 | */ 94 | public abstract T read(Reader reader) throws IOException; 95 | } 96 | -------------------------------------------------------------------------------- /db.sql: -------------------------------------------------------------------------------- 1 | drop schema if exists demo; 2 | 3 | create schema demo; 4 | 5 | use demo; 6 | 7 | create table owner ( 8 | name varchar(20), 9 | primary key (name) 10 | ); 11 | 12 | insert into owner (name) values ('Harold'); 13 | insert into owner (name) values ('Gwen'); 14 | insert into owner (name) values ('Benny'); 15 | insert into owner (name) values ('Diane'); 16 | 17 | create table pet ( 18 | name varchar(20), 19 | owner varchar(20), 20 | species varchar(20), 21 | sex char(1), 22 | birth date, 23 | death date, 24 | primary key (name), 25 | foreign key (owner) references owner(name) 26 | ); 27 | 28 | insert into pet (name, owner, species, sex, birth, death) values ('Fluffy', 'Harold', 'cat', 'f', '1993-02-04', null); 29 | insert into pet (name, owner, species, sex, birth, death) values ('Claws', 'Gwen', 'cat', 'm', '1994-03-17', null); 30 | insert into pet (name, owner, species, sex, birth, death) values ('Buffy', 'Harold', 'dog', 'f', '1989-05-13', null); 31 | insert into pet (name, owner, species, sex, birth, death) values ('Fang', 'Benny', 'dog', 'm', '1990-08-27', null); 32 | insert into pet (name, owner, species, sex, birth, death) values ('Bowser', 'Diane', 'dog', 'm', '1979-08-31', '1995-07-29'); 33 | insert into pet (name, owner, species, sex, birth, death) values ('Chirpy', 'Gwen', 'bird', 'f', '1998-09-11', null); 34 | insert into pet (name, owner, species, sex, birth, death) values ('Whistler', 'Gwen', 'bird', null, '1997-12-09', null); 35 | insert into pet (name, owner, species, sex, birth, death) values ('Slim', 'Benny', 'snake', 'm', '1996-04-29', null); 36 | 37 | create table item ( 38 | id int not null auto_increment, 39 | description varchar(1024) not null, 40 | price double not null, 41 | size tinyint not null, 42 | color varchar(128) not null, 43 | weight double not null, 44 | created bigint not null, 45 | primary key (id) 46 | ); 47 | 48 | create table bulk_upload_test ( 49 | id int not null auto_increment, 50 | text1 varchar(32) not null, 51 | text2 varchar(16) not null, 52 | number1 integer not null, 53 | number2 double not null, 54 | number3 double not null, 55 | primary key (id) 56 | ); 57 | 58 | create table whitespace_test ( 59 | value varchar(32) not null 60 | ); 61 | 62 | create table temporal_accessor_test ( 63 | id int not null auto_increment, 64 | local_date date not null, 65 | local_time time not null, 66 | instant timestamp(6) not null, 67 | primary key (id) 68 | ); 69 | 70 | create table json_test ( 71 | id int not null auto_increment, 72 | list varchar(1024) not null, 73 | map varchar(1024) not null, 74 | record varchar(1024) not null, 75 | primary key (id) 76 | ); 77 | 78 | create table xml_test ( 79 | id int not null auto_increment, 80 | xml varchar(1024) not null, 81 | primary key (id) 82 | ); 83 | 84 | drop user if exists 'demo'@'%'; 85 | 86 | create user 'demo'@'%' identified by 'demo123!'; 87 | grant select, insert, update, delete on demo.* to 'demo'@'%'; 88 | flush privileges; 89 | -------------------------------------------------------------------------------- /kilo-client/src/test/java/org/httprpc/kilo/util/concurrent/PipeTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.util.concurrent; 16 | 17 | import org.junit.jupiter.api.AfterAll; 18 | import org.junit.jupiter.api.BeforeAll; 19 | import org.junit.jupiter.api.Test; 20 | 21 | import java.util.concurrent.ExecutorService; 22 | import java.util.concurrent.Executors; 23 | import java.util.stream.StreamSupport; 24 | 25 | import static org.httprpc.kilo.util.Collections.*; 26 | import static org.junit.jupiter.api.Assertions.*; 27 | 28 | public class PipeTest { 29 | private static ExecutorService executorService = null; 30 | 31 | @BeforeAll 32 | public static void setUpClass() { 33 | executorService = Executors.newSingleThreadExecutor(); 34 | } 35 | 36 | @AfterAll 37 | public static void tearDownClass() { 38 | executorService.shutdown(); 39 | } 40 | 41 | @Test 42 | public void testBoundedPipe() { 43 | testPipe(new Pipe<>(1)); 44 | } 45 | 46 | @Test 47 | public void testBoundedPipeWithTimeout() { 48 | testPipe(new Pipe<>(1, 60000)); 49 | } 50 | 51 | @Test 52 | public void testUnboundedPipe() { 53 | testPipe(new Pipe<>()); 54 | } 55 | 56 | @Test 57 | public void testUnboundedPipeWithTimeout() { 58 | testPipe(new Pipe<>(Integer.MAX_VALUE, 60000)); 59 | } 60 | 61 | private void testPipe(Pipe pipe) { 62 | var expectedValues = listOf(0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89); 63 | 64 | executorService.submit(() -> pipe.submit(expectedValues.stream())); 65 | 66 | assertEquals(expectedValues, StreamSupport.stream(pipe.spliterator(), false).toList()); 67 | } 68 | 69 | @Test 70 | public void testPollTimeout() { 71 | var pipe = new Pipe(1, 100); 72 | 73 | executorService.submit(() -> { 74 | // No-op 75 | }); 76 | 77 | assertThrows(TimeoutException.class, () -> pipe.iterator().hasNext()); 78 | } 79 | 80 | @Test 81 | public void testOfferTimeout() throws Exception { 82 | var pipe = new Pipe(1, 100); 83 | 84 | var future = executorService.submit(() -> { 85 | try { 86 | pipe.submit(listOf("abc").stream()); 87 | } catch (TimeoutException exception) { 88 | return true; 89 | } 90 | 91 | return false; 92 | }); 93 | 94 | assertTrue(future.get()); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /kilo-test/src/test/java/org/httprpc/kilo/test/XMLTransformTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import org.httprpc.kilo.io.TemplateEncoder; 18 | import org.httprpc.kilo.xml.ElementAdapter; 19 | import org.w3c.dom.Document; 20 | 21 | import javax.xml.transform.Transformer; 22 | import javax.xml.transform.TransformerFactory; 23 | import javax.xml.transform.stream.StreamResult; 24 | import javax.xml.transform.stream.StreamSource; 25 | import java.nio.file.Files; 26 | import java.nio.file.Paths; 27 | 28 | public class XMLTransformTest { 29 | public static void main(String[] args) throws Exception { 30 | var xmlTransformTest = new XMLTransformTest(); 31 | 32 | var t0 = System.currentTimeMillis(); 33 | 34 | xmlTransformTest.transformXML1(); 35 | 36 | var t1 = System.currentTimeMillis(); 37 | 38 | System.out.printf("%s %dms\n", Transformer.class.getSimpleName(), (t1 - t0)); 39 | 40 | xmlTransformTest.transformXML2(); 41 | 42 | var t2 = System.currentTimeMillis(); 43 | 44 | System.out.printf("%s %dms\n", TemplateEncoder.class.getSimpleName(), (t2 - t1)); 45 | } 46 | 47 | private void transformXML1() throws Exception { 48 | var source = new StreamSource(getClass().getResourceAsStream("breakfast-menu.xslt")); 49 | 50 | var transformer = TransformerFactory.newInstance().newTransformer(source); 51 | 52 | var xmlSource = new StreamSource(getClass().getResourceAsStream("breakfast-menu.xml")); 53 | 54 | var outputFile = Paths.get(System.getProperty("user.home"), "breakfast-menu-1.html"); 55 | 56 | try (var outputStream = Files.newOutputStream(outputFile)) { 57 | transformer.transform(xmlSource, new StreamResult(outputStream)); 58 | } 59 | } 60 | 61 | private void transformXML2() throws Exception { 62 | var documentBuilder = ElementAdapter.newDocumentBuilder(); 63 | 64 | Document document; 65 | try (var inputStream = getClass().getResourceAsStream("breakfast-menu.xml")) { 66 | document = documentBuilder.parse(inputStream); 67 | } 68 | 69 | var templateEncoder = new TemplateEncoder(getClass(), "breakfast-menu.html"); 70 | 71 | var outputFile = Paths.get(System.getProperty("user.home"), "breakfast-menu-2.html"); 72 | 73 | try (var outputStream = Files.newOutputStream(outputFile)) { 74 | templateEncoder.write(new ElementAdapter(document.getDocumentElement()), outputStream); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | 74 | 75 | @rem Execute Gradle 76 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* 77 | 78 | :end 79 | @rem End local scope for the variables with windows NT shell 80 | if %ERRORLEVEL% equ 0 goto mainEnd 81 | 82 | :fail 83 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 84 | rem the _cmd.exe /c_ return code! 85 | set EXIT_CODE=%ERRORLEVEL% 86 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 87 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 88 | exit /b %EXIT_CODE% 89 | 90 | :mainEnd 91 | if "%OS%"=="Windows_NT" endlocal 92 | 93 | :omega 94 | -------------------------------------------------------------------------------- /kilo-test/src/test/java/org/httprpc/kilo/test/CatalogTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import org.httprpc.kilo.WebServiceProxy; 18 | import org.httprpc.kilo.beans.BeanAdapter; 19 | import org.junit.jupiter.api.Test; 20 | 21 | import java.io.IOException; 22 | import java.net.URI; 23 | 24 | import static org.httprpc.kilo.util.Collections.*; 25 | import static org.junit.jupiter.api.Assertions.*; 26 | 27 | public class CatalogTest { 28 | private static final URI baseURI = URI.create("http://localhost:8080/kilo-test/"); 29 | 30 | @Test 31 | public void testCatalog() throws IOException { 32 | var catalogServiceProxy = WebServiceProxy.of(CatalogServiceProxy.class, baseURI); 33 | 34 | var item = BeanAdapter.coerce(mapOf(), ItemDetail.class); 35 | 36 | item.setDescription("abc"); 37 | item.setPrice(150.0); 38 | item.setSize(Size.MEDIUM); 39 | item.setColor("red"); 40 | item.setWeight(5.0); 41 | 42 | item = catalogServiceProxy.addItem(item); 43 | 44 | assertNotNull(item); 45 | 46 | var itemID = item.getID(); 47 | 48 | assertNotNull(itemID); 49 | 50 | assertEquals("abc", item.getDescription()); 51 | assertEquals(150.0, item.getPrice()); 52 | assertEquals(Size.MEDIUM, item.getSize()); 53 | assertEquals(5.0, item.getWeight()); 54 | 55 | assertNotNull(item.getCreated()); 56 | 57 | item = catalogServiceProxy.getItem(itemID); 58 | 59 | assertNotNull(item); 60 | 61 | assertEquals(itemID, item.getID()); 62 | assertEquals("abc", item.getDescription()); 63 | assertEquals(150.0, item.getPrice()); 64 | assertEquals(Size.MEDIUM, item.getSize()); 65 | assertEquals(5.0, item.getWeight()); 66 | 67 | item.setDescription("xyz"); 68 | item.setPrice(300.0); 69 | item.setSize(Size.LARGE); 70 | item.setColor("blue"); 71 | item.setWeight(10.0); 72 | 73 | item = catalogServiceProxy.updateItem(item.getID(), item); 74 | 75 | assertNotNull(item); 76 | 77 | assertEquals(itemID, item.getID()); 78 | assertEquals("xyz", item.getDescription()); 79 | assertEquals(300.0, item.getPrice()); 80 | assertEquals(Size.LARGE, item.getSize()); 81 | assertEquals("blue", item.getColor()); 82 | assertEquals(10.0, item.getWeight()); 83 | 84 | assertNotNull(catalogServiceProxy.getItems().stream() 85 | .filter(result -> result.getID().equals(itemID)) 86 | .findAny().orElse(null)); 87 | 88 | catalogServiceProxy.deleteItem(item.getID()); 89 | 90 | assertNull(catalogServiceProxy.getItems().stream() 91 | .filter(result -> result.getID().equals(itemID)) 92 | .findAny().orElse(null)); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /kilo-test/src/test/resources/org/httprpc/kilo/test/math.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Math example service.", 3 | "endpoints": [ 4 | { 5 | "operations": [ 6 | { 7 | "bodyParameter": null, 8 | "deprecated": false, 9 | "description": "Calculates the sum of two numbers.", 10 | "formData": false, 11 | "method": "GET", 12 | "parameters": true, 13 | "pathParameters": [ 14 | ], 15 | "produces": { 16 | "elementType": null, 17 | "intrinsic": true, 18 | "iterable": false, 19 | "keyType": null, 20 | "map": false, 21 | "name": "double", 22 | "valueType": null 23 | }, 24 | "queryParameters": [ 25 | { 26 | "description": "The first number.", 27 | "name": "a", 28 | "required": false, 29 | "type": { 30 | "elementType": null, 31 | "intrinsic": true, 32 | "iterable": false, 33 | "keyType": null, 34 | "map": false, 35 | "name": "double", 36 | "valueType": null 37 | } 38 | }, 39 | { 40 | "description": "The second number.", 41 | "name": "b", 42 | "required": false, 43 | "type": { 44 | "elementType": null, 45 | "intrinsic": true, 46 | "iterable": false, 47 | "keyType": null, 48 | "map": false, 49 | "name": "double", 50 | "valueType": null 51 | } 52 | } 53 | ] 54 | }, 55 | { 56 | "bodyParameter": null, 57 | "deprecated": false, 58 | "description": "Calculates the sum of a list of numbers.", 59 | "formData": false, 60 | "method": "GET", 61 | "parameters": true, 62 | "pathParameters": [ 63 | ], 64 | "produces": { 65 | "elementType": null, 66 | "intrinsic": true, 67 | "iterable": false, 68 | "keyType": null, 69 | "map": false, 70 | "name": "double", 71 | "valueType": null 72 | }, 73 | "queryParameters": [ 74 | { 75 | "description": "The numbers to add.", 76 | "name": "values", 77 | "required": false, 78 | "type": { 79 | "elementType": { 80 | "elementType": null, 81 | "intrinsic": true, 82 | "iterable": false, 83 | "keyType": null, 84 | "map": false, 85 | "name": "Double", 86 | "valueType": null 87 | }, 88 | "intrinsic": true, 89 | "iterable": true, 90 | "keyType": null, 91 | "map": false, 92 | "name": "Iterable", 93 | "valueType": null 94 | } 95 | } 96 | ] 97 | } 98 | ], 99 | "path": "/math/sum" 100 | } 101 | ], 102 | "enumerations": [ 103 | ], 104 | "path": "/math", 105 | "structures": [ 106 | ] 107 | } -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/io/PagedReader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.io; 16 | 17 | import java.io.IOException; 18 | import java.io.Reader; 19 | import java.util.ArrayList; 20 | import java.util.Deque; 21 | import java.util.LinkedList; 22 | import java.util.List; 23 | 24 | class PagedReader extends Reader { 25 | private Reader reader; 26 | private int pageSize; 27 | 28 | private int position = 0; 29 | private int count = 0; 30 | 31 | private boolean endOfFile = false; 32 | 33 | private List pages = new ArrayList<>(); 34 | private Deque marks = new LinkedList<>(); 35 | 36 | private static final int DEFAULT_PAGE_SIZE = 1024; 37 | private static final int EOF = -1; 38 | 39 | PagedReader(Reader reader) { 40 | this(reader, DEFAULT_PAGE_SIZE); 41 | } 42 | 43 | PagedReader(Reader reader, int pageSize) { 44 | this.reader = reader; 45 | this.pageSize = pageSize; 46 | } 47 | 48 | @Override 49 | public int read() throws IOException { 50 | int c; 51 | if (position < count) { 52 | c = pages.get(position / pageSize)[position % pageSize]; 53 | 54 | position++; 55 | } else if (!endOfFile) { 56 | c = reader.read(); 57 | 58 | if (c != EOF) { 59 | if (position / pageSize == pages.size()) { 60 | pages.add(new char[pageSize]); 61 | } 62 | 63 | pages.getLast()[position % pageSize] = (char)c; 64 | 65 | position++; 66 | count++; 67 | } else { 68 | endOfFile = true; 69 | } 70 | } else { 71 | c = EOF; 72 | } 73 | 74 | return c; 75 | } 76 | 77 | @Override 78 | public int read(char[] cbuf, int off, int len) throws IOException { 79 | var c = 0; 80 | var n = 0; 81 | 82 | for (var i = off; i < cbuf.length && n < len; i++) { 83 | c = read(); 84 | 85 | if (c == EOF) { 86 | break; 87 | } 88 | 89 | cbuf[i] = (char)c; 90 | 91 | n++; 92 | } 93 | 94 | return c == EOF && n == 0 ? EOF : n; 95 | } 96 | 97 | @Override 98 | public boolean ready() throws IOException { 99 | return position < count || reader.ready(); 100 | } 101 | 102 | @Override 103 | public void mark(int readAheadLimit) { 104 | marks.push(position); 105 | } 106 | 107 | @Override 108 | public boolean markSupported() { 109 | return true; 110 | } 111 | 112 | @Override 113 | public void reset() { 114 | if (marks.isEmpty()) { 115 | position = 0; 116 | } else { 117 | position = marks.pop(); 118 | } 119 | } 120 | 121 | @Override 122 | public void close() throws IOException { 123 | reader.close(); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/util/Optionals.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.util; 16 | 17 | import java.util.function.Consumer; 18 | import java.util.function.Function; 19 | import java.util.function.Supplier; 20 | 21 | /** 22 | * Provides static utility methods for working with optional values. 23 | */ 24 | public class Optionals { 25 | private Optionals() { 26 | } 27 | 28 | /** 29 | * Returns either an optional value or the value produced by the given 30 | * supplier. 31 | * 32 | * @param 33 | * The value type. 34 | * 35 | * @param value 36 | * The optional value. 37 | * 38 | * @param supplier 39 | * The supplier to invoke if the provided value is {@code null}. 40 | * 41 | * @return 42 | * The provided value, or the value produced by the supplier if the value 43 | * was {@code null}. 44 | */ 45 | public static T coalesce(T value, Supplier supplier) { 46 | if (supplier == null) { 47 | throw new IllegalArgumentException(); 48 | } 49 | 50 | return (value != null) ? value : supplier.get(); 51 | } 52 | 53 | /** 54 | * Applies a mapping function to an optional value. 55 | * 56 | * @param 57 | * The value type. 58 | * 59 | * @param 60 | * The type produced by the mapping function. 61 | * 62 | * @param value 63 | * The optional value. 64 | * 65 | * @param transform 66 | * The mapping function to apply if the provided value is not {@code null}. 67 | * 68 | * @return 69 | * The result of applying the mapping function to the provided value, or 70 | * {@code null} if the value was {@code null}. 71 | */ 72 | public static U map(T value, Function transform) { 73 | if (transform == null) { 74 | throw new IllegalArgumentException(); 75 | } 76 | 77 | return (value != null) ? transform.apply(value) : null; 78 | } 79 | 80 | /** 81 | * Performs an action on an optional value. 82 | * 83 | * @param 84 | * The value type. 85 | * 86 | * @param value 87 | * The optional value. 88 | * 89 | * @param action 90 | * The action to perform if the provided value is not {@code null}. 91 | */ 92 | public static void perform(T value, Consumer action) { 93 | if (action == null) { 94 | throw new IllegalArgumentException(); 95 | } 96 | 97 | if (value != null) { 98 | action.accept(value); 99 | } 100 | } 101 | 102 | /** 103 | * Casts an optional value to a given type. 104 | * 105 | * @param 106 | * The target type. 107 | * 108 | * @param value 109 | * The optional value. 110 | * 111 | * @param type 112 | * The target type. 113 | * 114 | * @return 115 | * The value as an instance of the target type, or {@code null} if the 116 | * value is {@code null} or cannot be cast to the target type. 117 | */ 118 | public static T cast(Object value, Class type) { 119 | if (type == null) { 120 | throw new IllegalArgumentException(); 121 | } 122 | 123 | if (value != null && type.isAssignableFrom(value.getClass())) { 124 | return type.cast(value); 125 | } else { 126 | return null; 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /kilo-test/src/main/java/org/httprpc/kilo/test/PetService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import jakarta.servlet.annotation.WebServlet; 18 | import org.httprpc.kilo.RequestMethod; 19 | import org.httprpc.kilo.Required; 20 | import org.httprpc.kilo.ResourcePath; 21 | import org.httprpc.kilo.beans.BeanAdapter; 22 | import org.httprpc.kilo.io.CSVEncoder; 23 | import org.httprpc.kilo.io.JSONEncoder; 24 | import org.httprpc.kilo.io.TemplateEncoder; 25 | import org.httprpc.kilo.sql.QueryBuilder; 26 | 27 | import java.io.IOException; 28 | import java.sql.SQLException; 29 | import java.util.List; 30 | import java.util.ResourceBundle; 31 | 32 | import static org.httprpc.kilo.util.Collections.*; 33 | 34 | @WebServlet(urlPatterns = {"/pets/*"}, loadOnStartup = 1) 35 | public class PetService extends AbstractDatabaseService { 36 | @RequestMethod("GET") 37 | public List getPets(@Required String owner) throws SQLException { 38 | var queryBuilder = new QueryBuilder(); 39 | 40 | queryBuilder.appendLine("select * from pet where owner = :owner order by name"); 41 | 42 | try (var statement = queryBuilder.prepare(getConnection()); 43 | var results = queryBuilder.executeQuery(statement, mapOf( 44 | entry("owner", owner) 45 | ))) { 46 | return results.stream().map(result -> BeanAdapter.coerce(result, Pet.class)).toList(); 47 | } 48 | } 49 | 50 | @RequestMethod("GET") 51 | @ResourcePath("stream") 52 | public void getPetsStream(@Required String owner) throws SQLException, IOException { 53 | var response = getResponse(); 54 | 55 | var accept = getRequest().getHeader("Accept"); 56 | 57 | if (accept == null) { 58 | throw new UnsupportedOperationException(); 59 | } 60 | 61 | var queryBuilder = QueryBuilder.select(Pet.class) 62 | .filterByForeignKey(Owner.class, "owner") 63 | .ordered(true); 64 | 65 | try (var statement = queryBuilder.prepare(getConnection()); 66 | var results = queryBuilder.executeQuery(statement, mapOf( 67 | entry("owner", owner) 68 | ))) { 69 | if (accept.equalsIgnoreCase(APPLICATION_JSON)) { 70 | response.setContentType(APPLICATION_JSON); 71 | 72 | var jsonEncoder = new JSONEncoder(); 73 | 74 | jsonEncoder.write(results, response.getOutputStream()); 75 | } else if (accept.equalsIgnoreCase(TEXT_CSV)) { 76 | response.setContentType(TEXT_CSV); 77 | 78 | var csvEncoder = new CSVEncoder(listOf("name", "species", "sex", "birth", "death")); 79 | 80 | csvEncoder.setResourceBundle(ResourceBundle.getBundle(getClass().getName(), getRequest().getLocale())); 81 | 82 | csvEncoder.write(results, response.getOutputStream()); 83 | } else if (accept.equalsIgnoreCase(TEXT_HTML)) { 84 | response.setContentType(TEXT_HTML); 85 | 86 | var templateEncoder = new TemplateEncoder(getClass(), "pets.html"); 87 | 88 | templateEncoder.setResourceBundle(ResourceBundle.getBundle(getClass().getName(), getRequest().getLocale())); 89 | 90 | templateEncoder.write(results, response.getOutputStream()); 91 | } else if (accept.equalsIgnoreCase(TEXT_XML)) { 92 | response.setContentType(TEXT_XML); 93 | 94 | var templateEncoder = new TemplateEncoder(getClass(), "pets.xml"); 95 | 96 | templateEncoder.write(results, response.getOutputStream()); 97 | } else { 98 | throw new UnsupportedOperationException(); 99 | } 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /kilo-client/src/test/java/org/httprpc/kilo/util/CollectionsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.util; 16 | 17 | import org.junit.jupiter.api.Test; 18 | 19 | import java.util.ArrayList; 20 | import java.util.HashMap; 21 | import java.util.HashSet; 22 | 23 | import static org.httprpc.kilo.util.Collections.*; 24 | import static org.junit.jupiter.api.Assertions.*; 25 | 26 | public class CollectionsTest { 27 | @Test 28 | public void testListOf() { 29 | var expected = new ArrayList(3); 30 | 31 | expected.add(1); 32 | expected.add(2); 33 | expected.add(3); 34 | 35 | var actual = listOf(1, 2, 3); 36 | 37 | assertEquals(expected, actual); 38 | 39 | actual.remove(2); 40 | 41 | assertEquals(2, actual.size()); 42 | } 43 | 44 | @Test 45 | public void testImmutableListOf() { 46 | var list = immutableListOf(1, 2, 3); 47 | 48 | assertThrows(UnsupportedOperationException.class, () -> list.add(4)); 49 | } 50 | 51 | @Test 52 | public void testMapOf() { 53 | var expected = new HashMap(); 54 | 55 | expected.put("a", 1); 56 | expected.put("b", 2); 57 | expected.put("c", 3); 58 | 59 | var actual = mapOf( 60 | entry("a", 1), 61 | entry("b", 2), 62 | entry("c", 3) 63 | ); 64 | 65 | assertEquals(expected, actual); 66 | 67 | actual.remove("c"); 68 | 69 | assertEquals(2, actual.size()); 70 | } 71 | 72 | @Test 73 | public void testImmutableMapOf() { 74 | var map = immutableMapOf( 75 | entry("a", 1), 76 | entry("b", 2), 77 | entry("c", 3) 78 | ); 79 | 80 | assertThrows(UnsupportedOperationException.class, () -> map.put("d", 4)); 81 | } 82 | 83 | @Test 84 | public void testSortedMapOf() { 85 | var sortedMap = sortedMapOf( 86 | entry("c", 3), 87 | entry("b", 2), 88 | entry("a", 1) 89 | ); 90 | 91 | assertEquals(listOf(1, 2, 3), new ArrayList<>(sortedMap.values())); 92 | } 93 | 94 | @Test 95 | public void testSetOf() { 96 | var expected = new HashSet(3); 97 | 98 | expected.add(1); 99 | expected.add(2); 100 | expected.add(3); 101 | 102 | var actual = setOf(1, 2, 3); 103 | 104 | assertEquals(expected, actual); 105 | 106 | actual.remove(2); 107 | 108 | assertEquals(2, actual.size()); 109 | } 110 | 111 | @Test 112 | public void testImmutableSetOf() { 113 | var set = immutableSetOf(1, 2, 3); 114 | 115 | assertThrows(UnsupportedOperationException.class, () -> set.add(4)); 116 | } 117 | 118 | @Test 119 | public void testSortedSetOf() { 120 | var sortedSet = sortedSetOf(3, 2, 1); 121 | 122 | assertEquals(listOf(1, 2, 3), new ArrayList<>(sortedSet)); 123 | 124 | } 125 | 126 | @Test 127 | public void testEmptyListOf() { 128 | var list1 = java.util.Collections.emptyList(); 129 | var list2 = emptyListOf(Integer.class); 130 | 131 | assertTrue(list2.isEmpty()); 132 | assertEquals(list1, list2); 133 | } 134 | 135 | @Test 136 | public void testEmptyMapOf() { 137 | var map1 = java.util.Collections.emptyMap(); 138 | var map2 = emptyMapOf(String.class, Integer.class); 139 | 140 | assertTrue(map2.isEmpty()); 141 | assertEquals(map1, map2); 142 | } 143 | 144 | @Test 145 | public void testEmptySetOf() { 146 | var set1 = java.util.Collections.emptySet(); 147 | var set2 = emptySetOf(String.class); 148 | 149 | assertTrue(set2.isEmpty()); 150 | assertEquals(set1, set2); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /kilo-test/src/main/java/org/httprpc/kilo/test/CatalogService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import jakarta.servlet.annotation.WebServlet; 18 | import org.httprpc.kilo.Creates; 19 | import org.httprpc.kilo.Description; 20 | import org.httprpc.kilo.RequestMethod; 21 | import org.httprpc.kilo.ResourcePath; 22 | import org.httprpc.kilo.beans.BeanAdapter; 23 | import org.httprpc.kilo.sql.QueryBuilder; 24 | 25 | import java.sql.SQLException; 26 | import java.util.Date; 27 | import java.util.List; 28 | 29 | import static org.httprpc.kilo.util.Collections.*; 30 | 31 | @WebServlet(urlPatterns = {"/catalog/*"}, loadOnStartup = 1) 32 | @Description("Catalog example service.") 33 | public class CatalogService extends AbstractDatabaseService { 34 | @RequestMethod("GET") 35 | @ResourcePath("items") 36 | @Description("Returns a list of all items in the catalog.") 37 | public List getItems() throws SQLException { 38 | var queryBuilder = QueryBuilder.select(Item.class).ordered(true); 39 | 40 | try (var statement = queryBuilder.prepare(getConnection()); 41 | var results = queryBuilder.executeQuery(statement)) { 42 | return results.stream().map(result -> BeanAdapter.coerce(result, Item.class)).toList(); 43 | } 44 | } 45 | 46 | @RequestMethod("GET") 47 | @ResourcePath("items/?") 48 | @Description("Returns detailed information about a specific item.") 49 | public ItemDetail getItem( 50 | @Description("The item ID.") Integer itemID 51 | ) throws SQLException { 52 | var queryBuilder = QueryBuilder.select(ItemDetail.class).filterByPrimaryKey("itemID"); 53 | 54 | try (var statement = queryBuilder.prepare(getConnection()); 55 | var results = queryBuilder.executeQuery(statement, mapOf( 56 | entry("itemID", itemID) 57 | ))) { 58 | return results.stream().findFirst().map(result -> BeanAdapter.coerce(result, ItemDetail.class)).orElse(null); 59 | } 60 | } 61 | 62 | @RequestMethod("POST") 63 | @ResourcePath("items") 64 | @Description("Adds an item to the catalog.") 65 | @Creates 66 | public ItemDetail addItem( 67 | @Description("The item to add.") ItemDetail item 68 | ) throws SQLException { 69 | var queryBuilder = QueryBuilder.insert(ItemDetail.class); 70 | 71 | item.setCreated(new Date()); 72 | 73 | try (var statement = queryBuilder.prepare(getConnection())) { 74 | queryBuilder.executeUpdate(statement, new BeanAdapter(item)); 75 | } 76 | 77 | return getItem(queryBuilder.getGeneratedKey(0, Integer.class)); 78 | } 79 | 80 | @RequestMethod("PUT") 81 | @ResourcePath("items/?") 82 | @Description("Updates an item.") 83 | public ItemDetail updateItem( 84 | @Description("The item ID.") Integer itemID, 85 | @Description("The updated item.") ItemDetail item 86 | ) throws SQLException { 87 | item.setID(itemID); 88 | 89 | var queryBuilder = QueryBuilder.update(ItemDetail.class).filterByPrimaryKey("id"); 90 | 91 | try (var statement = queryBuilder.prepare(getConnection())) { 92 | queryBuilder.executeUpdate(statement, new BeanAdapter(item)); 93 | } 94 | 95 | return getItem(itemID); 96 | } 97 | 98 | @RequestMethod("DELETE") 99 | @ResourcePath("items/?") 100 | @Description("Deletes an item.") 101 | public void deleteItem( 102 | @Description("The item ID.") Integer itemID 103 | ) throws SQLException { 104 | var queryBuilder = QueryBuilder.delete(Item.class).filterByPrimaryKey("itemID"); 105 | 106 | try (var statement = queryBuilder.prepare(getConnection())) { 107 | queryBuilder.executeUpdate(statement, mapOf( 108 | entry("itemID", itemID) 109 | )); 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /kilo-test/src/main/java/org/httprpc/kilo/test/FilmService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import jakarta.servlet.annotation.WebServlet; 18 | import org.httprpc.kilo.Description; 19 | import org.httprpc.kilo.RequestMethod; 20 | import org.httprpc.kilo.ResourcePath; 21 | import org.httprpc.kilo.WebService; 22 | import org.httprpc.kilo.beans.BeanAdapter; 23 | import org.httprpc.kilo.sql.QueryBuilder; 24 | 25 | import java.sql.SQLException; 26 | import java.util.List; 27 | 28 | import static org.httprpc.kilo.util.Collections.*; 29 | import static org.httprpc.kilo.util.Optionals.*; 30 | 31 | @WebServlet(urlPatterns = {"/films/*"}, loadOnStartup = 1) 32 | @Description("Film example service.") 33 | public class FilmService extends WebService { 34 | @Override 35 | protected String getDataSourceName() { 36 | return "java:comp/env/jdbc/SakilaDB"; 37 | } 38 | 39 | @RequestMethod("GET") 40 | @Description("Returns a list of all films.") 41 | public List getFilms( 42 | @Description("An optional name pattern to match. An asterisk may be used as a wildcard.") String match 43 | ) throws SQLException { 44 | var queryBuilder = QueryBuilder.select(Film.class); 45 | 46 | if (match != null) { 47 | queryBuilder.filterByIndexLike("match"); 48 | } 49 | 50 | queryBuilder.ordered(true); 51 | 52 | try (var statement = queryBuilder.prepare(getConnection()); 53 | var results = queryBuilder.executeQuery(statement, mapOf( 54 | entry("match", map(match, value -> value.replace('*', '%'))) 55 | ))) { 56 | return results.stream().map(result -> BeanAdapter.coerce(result, Film.class)).toList(); 57 | } 58 | } 59 | 60 | @RequestMethod("GET") 61 | @ResourcePath("?") 62 | @Description("Returns detailed information about a specific film.") 63 | public FilmDetail getFilm( 64 | @Description("The film ID.") Integer filmID 65 | ) throws SQLException { 66 | var queryBuilder = QueryBuilder.select(FilmDetail.class).filterByPrimaryKey("filmID"); 67 | 68 | FilmDetail film; 69 | try (var statement = queryBuilder.prepare(getConnection()); 70 | var results = queryBuilder.executeQuery(statement, mapOf( 71 | entry("filmID", filmID) 72 | ))) { 73 | film = results.stream().findFirst().map(result -> BeanAdapter.coerce(result, FilmDetail.class)).orElseThrow(); 74 | } 75 | 76 | film.setActors(getActors(filmID)); 77 | film.setCategories(getCategories(filmID)); 78 | 79 | return film; 80 | } 81 | 82 | private List getActors(Integer filmID) throws SQLException { 83 | var queryBuilder = QueryBuilder.select(Actor.class) 84 | .join(FilmActor.class, Actor.class) 85 | .filterByForeignKey(FilmActor.class, Film.class, "filmID") 86 | .ordered(true); 87 | 88 | try (var statement = queryBuilder.prepare(getConnection()); 89 | var results = queryBuilder.executeQuery(statement, mapOf( 90 | entry("filmID", filmID) 91 | ))) { 92 | return results.stream().map(result -> BeanAdapter.coerce(result, Actor.class)).toList(); 93 | } 94 | } 95 | 96 | private List getCategories(Integer filmID) throws SQLException { 97 | var queryBuilder = QueryBuilder.select(Category.class) 98 | .join(FilmCategory.class, Category.class) 99 | .filterByForeignKey(FilmCategory.class, Film.class, "filmID") 100 | .ordered(true); 101 | 102 | try (var statement = queryBuilder.prepare(getConnection()); 103 | var results = queryBuilder.executeQuery(statement, mapOf( 104 | entry("filmID", filmID) 105 | ))) { 106 | return results.stream().map(result -> BeanAdapter.coerce(result, Category.class)).toList(); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/sql/ResultSetAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.sql; 16 | 17 | import java.sql.ResultSet; 18 | import java.sql.ResultSetMetaData; 19 | import java.sql.SQLException; 20 | import java.util.Iterator; 21 | import java.util.LinkedHashMap; 22 | import java.util.Map; 23 | import java.util.NoSuchElementException; 24 | import java.util.function.Function; 25 | import java.util.stream.Stream; 26 | import java.util.stream.StreamSupport; 27 | 28 | /** 29 | * Provides access to the contents of a JDBC result set via the 30 | * {@link Iterable} interface. 31 | */ 32 | public class ResultSetAdapter implements Iterable>, AutoCloseable { 33 | private ResultSet resultSet; 34 | private Map> transforms; 35 | 36 | private ResultSetMetaData resultSetMetaData; 37 | 38 | private Iterator> iterator = new Iterator<>() { 39 | Boolean hasNext = null; 40 | 41 | @Override 42 | public boolean hasNext() { 43 | if (hasNext == null) { 44 | try { 45 | hasNext = resultSet.next() ? Boolean.TRUE : Boolean.FALSE; 46 | } catch (SQLException exception) { 47 | throw new RuntimeException(exception); 48 | } 49 | } 50 | 51 | return hasNext; 52 | } 53 | 54 | @Override 55 | public Map next() { 56 | if (!hasNext()) { 57 | throw new NoSuchElementException(); 58 | } 59 | 60 | var row = new LinkedHashMap(); 61 | 62 | try { 63 | for (int i = 1, n = resultSetMetaData.getColumnCount(); i <= n; i++) { 64 | var key = resultSetMetaData.getColumnLabel(i); 65 | 66 | var value = resultSet.getObject(i); 67 | 68 | if (value != null) { 69 | switch (value) { 70 | case java.sql.Date date -> value = date.toLocalDate(); 71 | case java.sql.Time time -> value = time.toLocalTime(); 72 | case java.sql.Timestamp timestamp -> value = timestamp.toInstant(); 73 | default -> { 74 | var transform = transforms.get(key); 75 | 76 | if (transform != null) { 77 | value = transform.apply(value); 78 | } 79 | } 80 | } 81 | } 82 | 83 | row.put(key, value); 84 | } 85 | } catch (SQLException exception) { 86 | throw new RuntimeException(exception); 87 | } 88 | 89 | hasNext = null; 90 | 91 | return row; 92 | } 93 | }; 94 | 95 | ResultSetAdapter(ResultSet resultSet, Map> transforms) { 96 | this.resultSet = resultSet; 97 | this.transforms = transforms; 98 | 99 | try { 100 | resultSetMetaData = resultSet.getMetaData(); 101 | } catch (SQLException exception) { 102 | throw new RuntimeException(exception); 103 | } 104 | } 105 | 106 | /** 107 | * Returns the result set. 108 | * 109 | * @return 110 | * The result set. 111 | */ 112 | public ResultSet getResultSet() { 113 | return resultSet; 114 | } 115 | 116 | @Override 117 | public Iterator> iterator() { 118 | return iterator; 119 | } 120 | 121 | @Override 122 | public void close() throws SQLException { 123 | resultSet.close(); 124 | } 125 | 126 | /** 127 | * Returns a stream over the results. 128 | * 129 | * @return 130 | * A stream over the results. 131 | */ 132 | public Stream> stream() { 133 | return StreamSupport.stream(spliterator(), false).onClose(() -> { 134 | try { 135 | close(); 136 | } catch (SQLException exception) { 137 | throw new RuntimeException(exception); 138 | } 139 | }); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /kilo-test/src/main/java/org/httprpc/kilo/test/EmployeeService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import jakarta.servlet.ServletException; 18 | import jakarta.servlet.annotation.WebServlet; 19 | import org.hibernate.cfg.Configuration; 20 | import org.httprpc.kilo.RequestMethod; 21 | import org.httprpc.kilo.ResourcePath; 22 | import org.httprpc.kilo.WebService; 23 | import org.httprpc.kilo.beans.BeanAdapter; 24 | import org.httprpc.kilo.sql.QueryBuilder; 25 | import org.httprpc.kilo.util.concurrent.Pipe; 26 | 27 | import java.sql.SQLException; 28 | import java.util.List; 29 | import java.util.concurrent.ExecutorService; 30 | import java.util.concurrent.Executors; 31 | 32 | @WebServlet(urlPatterns = {"/employees/*"}, loadOnStartup = 1) 33 | public class EmployeeService extends WebService { 34 | private static ExecutorService executorService = null; 35 | 36 | @Override 37 | protected String getDataSourceName() { 38 | return "java:comp/env/jdbc/EmployeeDB"; 39 | } 40 | 41 | @Override 42 | public void init() throws ServletException { 43 | super.init(); 44 | 45 | executorService = Executors.newCachedThreadPool(); 46 | } 47 | 48 | @Override 49 | public void destroy() { 50 | executorService.shutdown(); 51 | 52 | super.destroy(); 53 | } 54 | 55 | @RequestMethod("GET") 56 | public List getEmployees() throws SQLException { 57 | var queryBuilder = QueryBuilder.select(Employee.class); 58 | 59 | try (var statement = queryBuilder.prepare(getConnection()); 60 | var results = queryBuilder.executeQuery(statement)) { 61 | return results.stream().map(result -> BeanAdapter.coerce(result, Employee.class)).toList(); 62 | } 63 | } 64 | 65 | @RequestMethod("GET") 66 | @ResourcePath("stream") 67 | public Iterable getEmployeesStream() { 68 | var pipe = new Pipe(4096, 15000); 69 | 70 | var connection = getConnection(); 71 | 72 | executorService.submit(() -> { 73 | var queryBuilder = QueryBuilder.select(Employee.class); 74 | 75 | try (var statement = queryBuilder.prepare(connection); 76 | var results = queryBuilder.executeQuery(statement)) { 77 | pipe.submit(results.stream().map(result -> BeanAdapter.coerce(result, Employee.class))); 78 | } catch (SQLException exception) { 79 | throw new RuntimeException(exception); 80 | } 81 | }); 82 | 83 | return pipe; 84 | } 85 | 86 | @RequestMethod("GET") 87 | @ResourcePath("hibernate") 88 | public List getEmployeesHibernate() { 89 | var configuration = new Configuration(); 90 | 91 | configuration.addAnnotatedClass(HibernateEmployee.class); 92 | 93 | try (var sessionFactory = configuration.configure().buildSessionFactory(); 94 | var session = sessionFactory.withOptions().connection(getConnection()).openSession()) { 95 | var criteriaQuery = session.getCriteriaBuilder().createQuery(Employee.class); 96 | var query = session.createQuery(criteriaQuery.select(criteriaQuery.from(HibernateEmployee.class))); 97 | 98 | return query.list(); 99 | } 100 | } 101 | 102 | @RequestMethod("GET") 103 | @ResourcePath("hibernate-stream") 104 | public Iterable getEmployeesHibernateStream() { 105 | var pipe = new Pipe(4096, 15000); 106 | 107 | var connection = getConnection(); 108 | 109 | executorService.submit(() -> { 110 | var configuration = new Configuration(); 111 | 112 | configuration.addAnnotatedClass(HibernateEmployee.class); 113 | 114 | try (var sessionFactory = configuration.configure().buildSessionFactory(); 115 | var session = sessionFactory.withOptions().connection(connection).openSession()) { 116 | var criteriaQuery = session.getCriteriaBuilder().createQuery(Employee.class); 117 | var query = session.createQuery(criteriaQuery.select(criteriaQuery.from(HibernateEmployee.class))); 118 | 119 | try (var stream = query.stream()) { 120 | pipe.submit(stream); 121 | } 122 | } 123 | }); 124 | 125 | return pipe; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /kilo-client/src/main/java/org/httprpc/kilo/util/concurrent/Pipe.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.util.concurrent; 16 | 17 | import java.util.Iterator; 18 | import java.util.NoSuchElementException; 19 | import java.util.concurrent.BlockingQueue; 20 | import java.util.concurrent.LinkedBlockingQueue; 21 | import java.util.concurrent.TimeUnit; 22 | import java.util.stream.Stream; 23 | 24 | /** 25 | * Provides a vehicle by which a producer thread can submit a sequence of 26 | * elements for retrieval by a consumer thread. 27 | * 28 | * @param 29 | * The element type. 30 | */ 31 | public class Pipe implements Iterable { 32 | private BlockingQueue queue; 33 | private int timeout; 34 | 35 | private Iterator iterator = new Iterator<>() { 36 | Boolean hasNext = null; 37 | E next = null; 38 | 39 | @Override 40 | @SuppressWarnings("unchecked") 41 | public boolean hasNext() { 42 | if (hasNext == null) { 43 | Object next; 44 | try { 45 | if (timeout == 0) { 46 | next = queue.take(); 47 | } else { 48 | next = queue.poll(timeout, TimeUnit.MILLISECONDS); 49 | 50 | if (next == null) { 51 | throw new TimeoutException("Poll timed out."); 52 | } 53 | } 54 | } catch (InterruptedException exception) { 55 | throw new RuntimeException(exception); 56 | } 57 | 58 | if (next == TERMINATOR) { 59 | hasNext = Boolean.FALSE; 60 | 61 | this.next = null; 62 | } else { 63 | hasNext = Boolean.TRUE; 64 | 65 | this.next = (E)next; 66 | } 67 | } 68 | 69 | return hasNext; 70 | } 71 | 72 | @Override 73 | public E next() { 74 | if (!hasNext()) { 75 | throw new NoSuchElementException(); 76 | } 77 | 78 | hasNext = null; 79 | 80 | return next; 81 | } 82 | }; 83 | 84 | private static final Object TERMINATOR = new Object(); 85 | 86 | /** 87 | * Constructs a new pipe. 88 | */ 89 | public Pipe() { 90 | this(Integer.MAX_VALUE); 91 | } 92 | 93 | /** 94 | * Constructs a new pipe. 95 | * 96 | * @param capacity 97 | * The pipe's capacity. 98 | */ 99 | public Pipe(int capacity) { 100 | this(capacity, 0); 101 | } 102 | 103 | /** 104 | * Constructs a new pipe. 105 | * 106 | * @param capacity 107 | * The pipe's capacity. 108 | * 109 | * @param timeout 110 | * The length of time to wait when submitting elements to or retrieving 111 | * elements from the pipe (in milliseconds), or 0 for no timeout. 112 | */ 113 | public Pipe(int capacity, int timeout) { 114 | queue = new LinkedBlockingQueue<>(capacity); 115 | 116 | if (timeout < 0) { 117 | throw new IllegalArgumentException("Invalid timeout."); 118 | } 119 | 120 | this.timeout = timeout; 121 | } 122 | 123 | /** 124 | * Submits elements to the pipe. 125 | * 126 | * @param stream 127 | * A stream containing the elements to submit. 128 | */ 129 | public void submit(Stream stream) { 130 | if (stream == null) { 131 | throw new IllegalArgumentException(); 132 | } 133 | 134 | var iterator = stream.iterator(); 135 | 136 | while (iterator.hasNext()) { 137 | submit(iterator.next()); 138 | } 139 | 140 | submit(TERMINATOR); 141 | } 142 | 143 | private void submit(Object value) { 144 | try { 145 | if (timeout == 0) { 146 | queue.put(value); 147 | } else { 148 | var result = queue.offer(value, timeout, TimeUnit.MILLISECONDS); 149 | 150 | if (!result) { 151 | throw new TimeoutException("Offer timed out."); 152 | } 153 | } 154 | } catch (InterruptedException exception) { 155 | throw new RuntimeException(exception); 156 | } 157 | } 158 | 159 | @Override 160 | public Iterator iterator() { 161 | return iterator; 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /kilo-test/src/test/java/org/httprpc/kilo/test/PetsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.httprpc.kilo.test; 16 | 17 | import org.httprpc.kilo.WebServiceProxy; 18 | import org.httprpc.kilo.io.JSONDecoder; 19 | import org.httprpc.kilo.io.TextDecoder; 20 | import org.junit.jupiter.api.Test; 21 | 22 | import java.io.IOException; 23 | import java.net.URI; 24 | import java.util.List; 25 | 26 | import static org.httprpc.kilo.util.Collections.*; 27 | import static org.junit.jupiter.api.Assertions.*; 28 | 29 | public class PetsTest { 30 | private static final URI baseURI = URI.create("http://localhost:8080/kilo-test/"); 31 | 32 | @Test 33 | public void testPets() throws IOException { 34 | testPetsJSON(); 35 | 36 | testPetsStreamJSON(); 37 | testPetsStreamCSV(); 38 | testPetsStreamHTML(); 39 | testPetsStreamXML(); 40 | } 41 | 42 | private void testPetsJSON() throws IOException { 43 | List expected; 44 | try (var inputStream = getClass().getResourceAsStream("pets.json")) { 45 | var jsonDecoder = new JSONDecoder(); 46 | 47 | expected = (List)jsonDecoder.read(inputStream); 48 | } 49 | 50 | var webServiceProxy = new WebServiceProxy("GET", baseURI.resolve("pets")); 51 | 52 | webServiceProxy.setArguments(mapOf( 53 | entry("owner", "Gwen") 54 | )); 55 | 56 | var actual = webServiceProxy.invoke(); 57 | 58 | assertEquals(expected, actual); 59 | } 60 | 61 | private void testPetsStreamJSON() throws IOException { 62 | List expected; 63 | try (var inputStream = getClass().getResourceAsStream("pets.json")) { 64 | var jsonDecoder = new JSONDecoder(); 65 | 66 | expected = (List)jsonDecoder.read(inputStream); 67 | } 68 | 69 | var webServiceProxy = new WebServiceProxy("GET", baseURI.resolve("pets/stream")); 70 | 71 | webServiceProxy.setArguments(mapOf( 72 | entry("owner", "Gwen") 73 | )); 74 | 75 | webServiceProxy.setHeaders(mapOf( 76 | entry("Accept", "application/json") 77 | )); 78 | 79 | var actual = webServiceProxy.invoke(); 80 | 81 | assertEquals(expected, actual); 82 | } 83 | 84 | private void testPetsStreamCSV() throws IOException { 85 | String expected; 86 | try (var inputStream = getClass().getResourceAsStream("pets.csv")) { 87 | var textDecoder = new TextDecoder(); 88 | 89 | expected = textDecoder.read(inputStream); 90 | } 91 | 92 | var webServiceProxy = new WebServiceProxy("GET", baseURI.resolve("pets/stream")); 93 | 94 | webServiceProxy.setArguments(mapOf( 95 | entry("owner", "Gwen") 96 | )); 97 | 98 | webServiceProxy.setHeaders(mapOf( 99 | entry("Accept", "text/csv") 100 | )); 101 | 102 | webServiceProxy.setResponseHandler((inputStream, contentType) -> { 103 | var textDecoder = new TextDecoder(); 104 | 105 | return textDecoder.read(inputStream); 106 | }); 107 | 108 | var actual = webServiceProxy.invoke(); 109 | 110 | assertEquals(expected, actual); 111 | } 112 | 113 | private void testPetsStreamHTML() throws IOException { 114 | testPetsStreamMarkup("pets.html", "text/html"); 115 | } 116 | 117 | private void testPetsStreamXML() throws IOException { 118 | testPetsStreamMarkup("pets.xml", "text/xml"); 119 | } 120 | 121 | private void testPetsStreamMarkup(String name, String mimeType) throws IOException { 122 | String expected; 123 | try (var inputStream = getClass().getResourceAsStream(name)) { 124 | var textDecoder = new TextDecoder(); 125 | 126 | expected = textDecoder.read(inputStream); 127 | } 128 | 129 | var webServiceProxy = new WebServiceProxy("GET", baseURI.resolve("pets/stream")); 130 | 131 | webServiceProxy.setArguments(mapOf( 132 | entry("owner", "Gwen") 133 | )); 134 | 135 | webServiceProxy.setHeaders(mapOf( 136 | entry("Accept", mimeType) 137 | )); 138 | 139 | webServiceProxy.setResponseHandler((inputStream, contentType) -> { 140 | var textDecoder = new TextDecoder(); 141 | 142 | return textDecoder.read(inputStream); 143 | }); 144 | 145 | var actual = webServiceProxy.invoke(); 146 | 147 | assertEquals(expected, actual); 148 | } 149 | } 150 | --------------------------------------------------------------------------------