Skip to content

Commit

Permalink
add, RESTEasy ClientHttpEngine customize example
Browse files Browse the repository at this point in the history
  • Loading branch information
kazuhira-r committed Sep 9, 2017
1 parent d065d76 commit 5512c2d
Show file tree
Hide file tree
Showing 9 changed files with 339 additions and 0 deletions.
43 changes: 43 additions & 0 deletions resteasy-customize-http-engine/build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name := "resteasy-customize-http-engine"

organization := "org.littlewings"

version := "0.0.1-SNAPSHOT"

scalaVersion := "2.12.3"

updateOptions := updateOptions.value.withCachedResolution(true)

scalacOptions ++= Seq("-Xlint", "-deprecation", "-unchecked", "-feature")

libraryDependencies ++= Seq(
// for RESTEasy Client
"org.jboss.resteasy" % "resteasy-client" % "3.1.4.Final" % Compile exclude("junit", "junit"),
"org.jboss.resteasy" % "resteasy-jackson2-provider" % "3.1.4.Final" % Compile,
"com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.8.9" % Compile,

// dependency retrieve
"org.jboss.resteasy" % "resteasy-jaxrs" % "3.1.4.Final" % Compile,
"org.jboss.logging" % "jboss-logging" % "3.3.0.Final" % Compile,
"org.jboss.logging" % "jboss-logging-annotations" % "2.0.1.Final" % Compile,
"org.jboss.logging" % "jboss-logging-processor" % "2.0.1.Final" % Compile,
"org.apache.httpcomponents" % "httpclient" % "4.5.2" % Compile,
"com.fasterxml.jackson.core" % "jackson-core" % "2.8.9" % Compile,
"com.fasterxml.jackson.core" % "jackson-annotations" % "2.8.9" % Compile,
"com.fasterxml.jackson.core" % "jackson-databind" % "2.8.9" % Compile,
"com.fasterxml.jackson.jaxrs" % "jackson-jaxrs-json-provider" % "2.8.9" % Compile,
"javax.activation" % "activation" % "1.1.1" % Compile,
"org.jboss.spec.javax.servlet" % "jboss-servlet-api_3.1_spec" % "1.0.0.Final" % Compile,
"org.jboss.spec.javax.annotation" % "jboss-annotations-api_1.2_spec" % "1.0.0.Final" % Compile,
"org.jboss.spec.javax.ws.rs" % "jboss-jaxrs-api_2.0_spec" % "1.0.1.Beta1" % Compile,
"commons-io" % "commons-io" % "2.5" % Compile,
"net.jcip" % "jcip-annotations" % "1.0" % Compile,

// for Customize HttpEngine
"com.squareup.okhttp3" % "okhttp" % "3.9.0" % Compile,

// for test
"org.jboss.resteasy" % "resteasy-netty4" % "3.1.4.Final" % Test,
"io.netty" % "netty-all" % "4.1.15.Final" % Test,
"org.scalatest" %% "scalatest" % "3.0.4" % Test
)
1 change: 1 addition & 0 deletions resteasy-customize-http-engine/project/build.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sbt.version = 1.0.1
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package org.littlewings.javaee7.resteasy

import java.io.{ByteArrayOutputStream, InputStream}
import javax.net.ssl.{HostnameVerifier, SSLContext}
import javax.ws.rs.core.MultivaluedMap

import okhttp3.{MediaType, OkHttpClient, Request, RequestBody}
import org.jboss.resteasy.client.jaxrs.ClientHttpEngine
import org.jboss.resteasy.client.jaxrs.internal.{ClientInvocation, ClientResponse}
import org.jboss.resteasy.util.CaseInsensitiveMap

import scala.beans.BeanProperty

class OkHttpEngine extends ClientHttpEngine {
val okHttpClient: OkHttpClient = new OkHttpClient

@BeanProperty
var sslContext: SSLContext = _

@BeanProperty
var hostnameVerifier: HostnameVerifier = _

override def invoke(request: ClientInvocation): ClientResponse = {
val okHttpRequestBuilder =
new Request.Builder()
.url(request.getUri.toURL)

val body = Option(request.getEntity).map { _ =>
val baos = new ByteArrayOutputStream
request.getDelegatingOutputStream.setDelegate(baos)
request.writeRequestBody(baos)
baos.close()
baos.toByteArray
}

val headers = request.getHeaders.asMap
headers.entrySet.forEach(header => header.getValue.forEach(okHttpRequestBuilder.addHeader(header.getKey, _)))

okHttpRequestBuilder.method(
request.getMethod,
body.map(RequestBody.create(MediaType.parse(request.getHeaders.getMediaType.toString), _)).getOrElse(null)
)

val okHttpRequest = okHttpRequestBuilder.build

val okHttpResponse = okHttpClient.newCall(okHttpRequest).execute()

val response = new ClientResponse(request.getClientConfiguration) {
var inputStream: InputStream = _

override def releaseConnection(): Unit = okHttpResponse.close()

override def setInputStream(is: InputStream): Unit = inputStream = is

override def getInputStream = {
if (inputStream == null) {
inputStream = okHttpResponse.body().byteStream()
}

inputStream
}
}

response.setStatus(okHttpResponse.code())

val responseHeaders: MultivaluedMap[String, String] = new CaseInsensitiveMap
val headerNames = okHttpResponse.headers.names
headerNames.forEach(name => okHttpResponse.headers(name).forEach(value => responseHeaders.add(name, value)))

response.setHeaders(responseHeaders)
response
}

override def close(): Unit = ()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.littlewings.javaee7.resteasy.ScalaObjectMapperProvider
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package org.littlewings.javaee7.resteasy

case class Book(isbn: String, title: String, price: Int)
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package org.littlewings.javaee7.resteasy

import javax.ws.rs.client.{ClientBuilder, Entity}
import javax.ws.rs.core.Response

import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder
import org.jboss.resteasy.client.jaxrs.engines.URLConnectionEngine
import org.scalatest.{FunSuite, Matchers}

class RestEasyHttpEngineSpec extends FunSuite with Matchers {
test("use standard engine") {
TestRestServer.withServer {
val client = ClientBuilder.newBuilder.build

try {
val response =
client
.target("http://localhost:8080/test/get")
.request
.get()

response.getStatus should be(Response.Status.OK.getStatusCode)
response.readEntity(classOf[Book]) should be(
Book("978-4798140926", "Java EE 7徹底入門 標準Javaフレームワークによる高信頼性Webシステムの構築", 4104)
)

response.close()
} finally {
client.close()
}
}
}

test("use java.net.HttpConnection engine") {
TestRestServer.withServer {
val client = new ResteasyClientBuilder().httpEngine(new URLConnectionEngine).build()

try {
val response =
client
.target("http://localhost:8080/test/get")
.request
.get()

response.getStatus should be(Response.Status.OK.getStatusCode)
response.readEntity(classOf[Book]) should be(
Book("978-4798140926", "Java EE 7徹底入門 標準Javaフレームワークによる高信頼性Webシステムの構築", 4104)
)

response.close()
} finally {
client.close()
}
}
}

test("use OkHttp engine") {
TestRestServer.withServer {
val client = new ResteasyClientBuilder().httpEngine(new OkHttpEngine).build()

try {
val response =
client
.target("http://localhost:8080/test/get")
.request
.get()

response.getStatus should be(Response.Status.OK.getStatusCode)
response.readEntity(classOf[Book]) should be(
Book("978-4798140926", "Java EE 7徹底入門 標準Javaフレームワークによる高信頼性Webシステムの構築", 4104)
)

response.close()
} finally {
client.close()
}
}
}

test("use OkHttp engine, post") {
TestRestServer.withServer {
val client = new ResteasyClientBuilder().httpEngine(new OkHttpEngine).build()

try {
val response =
client
.target("http://localhost:8080/test/post")
.request
.post(Entity.json(Book("978-4774183169", "パーフェクト Java EE", 3456)))

response.getStatus should be(Response.Status.OK.getStatusCode)
response.readEntity(classOf[Book]) should be(
Book("978-4774183169", "パーフェクト Java EE", 6912)
)

response.close()
} finally {
client.close()
}
}
}

test("use OkHttp engine, put") {
TestRestServer.withServer {
val client = new ResteasyClientBuilder().httpEngine(new OkHttpEngine).build()

try {
val response =
client
.target("http://localhost:8080/test/put")
.request
.put(Entity.json(Book("978-4774183169", "パーフェクト Java EE", 3456)))

response.getStatus should be(Response.Status.OK.getStatusCode)
response.readEntity(classOf[Book]) should be(
Book("978-4774183169", "パーフェクト Java EE", 6912)
)

response.close()
} finally {
client.close()
}
}
}

test("use OkHttp engine, delete") {
TestRestServer.withServer {
val client = new ResteasyClientBuilder().httpEngine(new OkHttpEngine).build()

try {
val response =
client
.target("http://localhost:8080/test/delete")
.request
.method("DELETE", Entity.json(Book("978-4774183169", "パーフェクト Java EE", 3456)))

response.getStatus should be(Response.Status.NO_CONTENT.getStatusCode)
response.getEntity should be(null)

response.close()
} finally {
client.close()
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.littlewings.javaee7.resteasy

import javax.ws.rs.{Consumes, Produces}
import javax.ws.rs.core.MediaType
import javax.ws.rs.ext.{ContextResolver, Provider}

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.scala.DefaultScalaModule

@Provider
@Consumes(Array(MediaType.APPLICATION_JSON))
@Produces(Array(MediaType.APPLICATION_JSON))
class ScalaObjectMapperProvider extends ContextResolver[ObjectMapper] {
override def getContext(typ: Class[_]): ObjectMapper = {
val objectMapper = new ObjectMapper
objectMapper.registerModule(DefaultScalaModule)
objectMapper
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package org.littlewings.javaee7.resteasy

import javax.ws.rs.core.MediaType
import javax.ws.rs._

import org.jboss.resteasy.plugins.server.netty.NettyJaxrsServer

object TestRestServer {
def withServer(fun: => Unit): Unit = {
val netty = new NettyJaxrsServer
val deployment = netty.getDeployment
deployment.setResourceClasses(java.util.Arrays.asList(classOf[TestResource].getName))
netty.setRootResourcePath("")
netty.setPort(8080)
netty.setDeployment(deployment)
netty.start()

try {
fun
} finally {
netty.stop()
}
}
}

@Path("test")
class TestResource {
@GET
@Path("get")
@Produces(Array(MediaType.APPLICATION_JSON))
def get: Book =
Book("978-4798140926", "Java EE 7徹底入門 標準Javaフレームワークによる高信頼性Webシステムの構築", 4104)

@POST
@Path("post")
@Consumes(Array(MediaType.APPLICATION_JSON))
@Produces(Array(MediaType.APPLICATION_JSON))
def post(book: Book): Book = book.copy(price = book.price * 2)

@PUT
@Path("put")
@Consumes(Array(MediaType.APPLICATION_JSON))
@Produces(Array(MediaType.APPLICATION_JSON))
def put(book: Book): Book = book.copy(price = book.price * 2)

@DELETE
@Path("delete")
@Consumes(Array(MediaType.APPLICATION_JSON))
@Produces(Array(MediaType.APPLICATION_JSON))
def delete(book: Book): Unit = ()
}

0 comments on commit 5512c2d

Please sign in to comment.