tech.sirwellington.alchemy.http.AlchemyMachineImpl.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of alchemy-http Show documentation
Show all versions of alchemy-http Show documentation
Part of the Alchemy Collection.
REST without the MESS.
Makes it dead-easy to call RESTful Web Services in Java.
It is designed for interactions with APIs written in REST and JSON.
/*
* Copyright © 2019. Sir Wellington.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package tech.sirwellington.alchemy.http
import com.google.gson.Gson
import org.slf4j.LoggerFactory
import tech.sirwellington.alchemy.annotations.access.Internal
import tech.sirwellington.alchemy.annotations.designs.StepMachineDesign
import tech.sirwellington.alchemy.annotations.designs.StepMachineDesign.Role.MACHINE
import tech.sirwellington.alchemy.arguments.Arguments.checkThat
import tech.sirwellington.alchemy.arguments.assertions.positiveLong
import tech.sirwellington.alchemy.http.AlchemyRequestSteps.OnFailure
import tech.sirwellington.alchemy.http.AlchemyRequestSteps.OnSuccess
import tech.sirwellington.alchemy.http.HttpAssertions.okResponse
import tech.sirwellington.alchemy.http.HttpAssertions.ready
import tech.sirwellington.alchemy.http.HttpAssertions.validResponseClass
import tech.sirwellington.alchemy.http.exceptions.AlchemyHttpException
import java.util.concurrent.Executor
/**
*
* @author SirWellington
*/
@Internal
@StepMachineDesign(role = MACHINE)
internal class AlchemyMachineImpl @JvmOverloads constructor(private val async: Executor,
private val gson: Gson,
private val requestExecutor: HttpRequestExecutor,
private val timeoutMillis: Long = Constants.DEFAULT_TIMEOUT) : AlchemyHttpStateMachine
{
init
{
checkThat(timeoutMillis).isA(positiveLong())
}
private val LOG = LoggerFactory.getLogger(AlchemyMachineImpl::class.java)
@Throws(IllegalArgumentException::class)
override fun begin(initialRequest: HttpRequest): AlchemyRequestSteps.Step1
{
val requestCopy = HttpRequest.copyOf(initialRequest)
LOG.debug("Beginning HTTP request {}", requestCopy)
return Step1Impl(this, requestCopy)
}
@Throws(IllegalArgumentException::class)
override fun jumpToStep2(request: HttpRequest): AlchemyRequestSteps.Step2
{
val requestCopy = HttpRequest.copyOf(request)
return Step2Impl(requestCopy, this, gson)
}
@Throws(IllegalArgumentException::class)
override fun jumpToStep3(request: HttpRequest): AlchemyRequestSteps.Step3
{
val requestCopy = HttpRequest.copyOf(request)
return Step3Impl(this, requestCopy)
}
@Throws(IllegalArgumentException::class)
override fun jumpToStep4(request: HttpRequest,
classOfResponseType: Class): AlchemyRequestSteps.Step4
{
checkThat(classOfResponseType).isA(validResponseClass())
val requestCopy = HttpRequest.copyOf(request)
return Step4Impl(this, requestCopy, classOfResponseType)
}
@Throws(IllegalArgumentException::class)
override fun jumpToStep5(request: HttpRequest,
classOfResponseType: Class,
successCallback: OnSuccess): AlchemyRequestSteps.Step5
{
checkThat(classOfResponseType).isA(validResponseClass())
val requestCopy = HttpRequest.copyOf(request)
return Step5Impl(this, requestCopy, classOfResponseType, successCallback)
}
override fun jumpToStep6(request: HttpRequest,
classOfResponseType: Class,
successCallback: OnSuccess,
failureCallback: OnFailure): AlchemyRequestSteps.Step6
{
checkThat(classOfResponseType).isA(validResponseClass())
val requestCopy = HttpRequest.copyOf(request)
return Step6Impl(this, requestCopy, classOfResponseType, successCallback, failureCallback)
}
override fun executeSync(request: HttpRequest, classOfResponseType: Class): ResponseType
{
LOG.debug("Executing synchronous HTTP Request {}", request)
checkThat(classOfResponseType).isA(validResponseClass())
checkThat(request).isA(ready())
val response = try
{
requestExecutor.execute(request, gson)
}
catch (ex: AlchemyHttpException)
{
throw ex
}
catch (ex: Exception)
{
LOG.error("Failed to execute request {}", request, ex)
throw AlchemyHttpException(request, ex)
}
checkThat(response)
.throwing { ex -> AlchemyHttpException(request, response, "Http Response not OK.") }
.isA(okResponse())
LOG.trace("HTTP Request {} successfully executed: {}", request, response)
return if (classOfResponseType == HttpResponse::class.java)
{
response as ResponseType
}
else if (classOfResponseType == String::class.java)
{
response.bodyAsString() as ResponseType
}
else
{
LOG.trace("Attempting to parse response {} as {}", response, classOfResponseType)
response.bodyAs(classOfResponseType)
}
}
override fun executeAsync(request: HttpRequest,
classOfResponseType: Class,
successCallback: OnSuccess,
failureCallback: OnFailure)
{
checkThat(request).isA(ready())
checkThat(classOfResponseType).isA(validResponseClass())
LOG.debug("Submitting Async HTTP Request {}", request)
async.execute block@ {
LOG.debug("Starting Async HTTP Request {}", request)
val response = try
{
executeSync(request, classOfResponseType)
}
catch (ex: AlchemyHttpException)
{
LOG.trace("Async request failed", ex)
failureCallback.handleError(ex)
return@block
}
catch (ex: Exception)
{
LOG.trace("Async request failed", ex)
failureCallback.handleError(AlchemyHttpException(ex))
return@block
}
try
{
successCallback.processResponse(response)
}
catch (ex: Exception)
{
val message = "Success Callback threw exception"
LOG.warn(message, ex)
failureCallback.handleError(AlchemyHttpException(message, ex))
}
}
}
override fun toString(): String
{
return "AlchemyMachineImpl(async=$async, timeoutMillis=$timeoutMillis)"
}
}