blob: d5d33d2e259ebc7e83702f60faaf314d2cd92473 [file] [log] [blame]
// ***************************************************************************************************************************
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
// * to you 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 org.apache.juneau.rest.mock2;
import java.util.*;
import org.apache.juneau.json.*;
import org.apache.juneau.marshall.*;
import org.apache.juneau.parser.*;
import org.apache.juneau.rest.annotation.*;
import org.apache.juneau.rest.client.*;
import org.apache.juneau.http.remote.*;
import org.apache.juneau.serializer.*;
/**
* Creates a mocked interface against a REST resource class to use for creating test remote resource interfaces.
*
* <ul class='seealso'>
* <li class='link'>{@doc juneau-rest-mock.MockRemoteResource}
* </ul>
*
* @param <T> The interface class.
*/
public class MockRemoteResource<T> {
private MockRest.Builder mrb;
private RestClientBuilder rcb = RestClient.create().json();
private final Class<T> intf;
/**
* Constructor.
*
* @param intf
* The remote interface annotated with {@link RemoteResource @RemoteResource}.
* @param impl
* The REST implementation bean or bean class annotated with {@link Rest @Rest}.
* <br>If a class, it must have a no-arg constructor.
*/
protected MockRemoteResource(Class<T> intf, Object impl) {
this.intf = intf;
mrb = MockRest.create(impl);
}
/**
* Create a new builder using the specified remote resource interface and REST implementation bean or bean class.
*
* <p>
* Uses {@link JsonSerializer#DEFAULT} and {@link JsonParser#DEFAULT} for serializing and parsing by default.
*
*
* @param intf
* The remote interface annotated with {@link RemoteResource @RemoteResource}.
* @param impl
* The REST implementation bean or bean class annotated with {@link Rest @Rest}.
* <br>If a class, it must have a no-arg constructor.
* @return A new builder.
*/
public static <T> MockRemoteResource<T> create(Class<T> intf, Object impl) {
return new MockRemoteResource<>(intf, impl);
}
/**
* Create a new builder using the specified remote resource interface and REST implementation bean or bean class.
*
* <p>
* Uses the serializer and parser defined on the specified marshall for serializing and parsing by default.
*
* @param intf
* The remote interface annotated with {@link RemoteResource @RemoteResource}.
* @param impl
* The REST implementation bean or bean class annotated with {@link Rest @Rest}.
* <br>If a class, it must have a no-arg constructor.
* @param m
* The marshall to use for serializing and parsing the HTTP bodies.
* @return A new builder.
*/
public static <T> MockRemoteResource<T> create(Class<T> intf, Object impl, Marshall m) {
return new MockRemoteResource<>(intf, impl).marshall(m);
}
/**
* Create a new builder using the specified remote resource interface and REST implementation bean or bean class.
*
* <p>
* Uses the serializer and parser defined on the specified marshall for serializing and parsing by default.
*
* @param intf
* The remote interface annotated with {@link RemoteResource @RemoteResource}.
* @param impl
* The REST implementation bean or bean class annotated with {@link Rest @Rest}.
* <br>If a class, it must have a no-arg constructor.
* @param s
* The serializer to use for serializing request bodies.
* <br>Can be <jk>null</jk> to force no serializer to be used and no <c>Content-Type</c> header.
* @param p
* The parser to use for parsing response bodies.
* <br>Can be <jk>null</jk> to force no parser to be used and no <c>Accept</c> header.
* @return A new builder.
*/
public static <T> MockRemoteResource<T> create(Class<T> intf, Object impl, Serializer s, Parser p) {
return new MockRemoteResource<>(intf, impl).serializer(s).parser(p);
}
/**
* Constructs a remote proxy interface based on the settings of this builder.
*
* @return A new remote proxy interface.
*/
public T build() {
MockRest mr = mrb.build();
return rcb.httpClientConnectionManager(new MockHttpClientConnectionManager(mr)).rootUrl("http://localhost").headers(mr.getHeaders()).build().getRemoteResource(intf);
}
/**
* Convenience method for getting a remote resource interface.
*
* <p>
* Uses {@link JsonSerializer#DEFAULT} and {@link JsonParser#DEFAULT} for serializing and parsing by default.
*
* <p>
* Equivalent to calling the following:
* <p class='bcode w800'>
* MockRemoteResource.<jsf>create</jsf>(intf, impl).build();
* </p>
*
* @param intf
* The remote interface annotated with {@link RemoteResource @RemoteResource}.
* @param impl
* The REST implementation bean or bean class annotated with {@link Rest @Rest}.
* <br>If a class, it must have a no-arg constructor.
* @return A new proxy interface.
*/
public static <T> T build(Class<T> intf, Object impl) {
return create(intf, impl).build();
}
/**
* Convenience method for getting a remote resource interface.
*
* <p>
* Uses the serializer and parser defined on the specified marshall for serializing and parsing by default.
*
* <p>
* Equivalent to calling the following:
* <p class='bcode w800'>
* MockRemoteResource.<jsf>create</jsf>(intf, impl).marshall(m).build();
* </p>
*
* @param intf
* The remote interface annotated with {@link RemoteResource @RemoteResource}.
* @param impl
* The REST implementation bean or bean class annotated with {@link Rest @Rest}.
* <br>If a class, it must have a no-arg constructor.
* @param m
* The marshall to use for serializing request bodies and parsing response bodies.
* <br>Can be <jk>null</jk> to force no serializer or parser to be used and no <c>Accept</c> or <c>Content-Type</c> header.
* @return A new proxy interface.
*/
public static <T> T build(Class<T> intf, Object impl, Marshall m) {
return create(intf, impl).marshall(m).build();
}
/**
* Convenience method for getting a remote resource interface.
*
* <p>
* Uses the specified serializer and parser for serializing and parsing by default.
*
* <p>
* Equivalent to calling the following:
* <p class='bcode w800'>
* MockRemoteResource.<jsf>create</jsf>(intf, impl).serializer(s).parser(p).build();
* </p>
*
* @param intf
* The remote interface annotated with {@link RemoteResource @RemoteResource}.
* @param impl
* The REST implementation bean or bean class annotated with {@link Rest @Rest}.
* <br>If a class, it must have a no-arg constructor.
* @param s
* The serializer to use for serializing request bodies.
* <br>Can be <jk>null</jk> to force no serializer to be used and no <c>Content-Type</c> header.
* @param p
* The parser to use for parsing response bodies.
* <br>Can be <jk>null</jk> to force no parser to be used and no <c>Accept</c> header.
* @return A new proxy interface.
*/
public static <T> T build(Class<T> intf, Object impl, Serializer s, Parser p) {
return create(intf, impl).serializer(s).parser(p).build();
}
/**
* Enable debug mode.
*
* @return This object (for method chaining).
*/
public MockRemoteResource<T> debug() {
mrb.debug();
rcb.debug();
return this;
}
/**
* Adds a header to every request.
*
* @param name The header name.
* @param value
* The header value.
* <br>Can be <jk>null</jk> (will be skipped).
* @return This object (for method chaining).
*/
public MockRemoteResource<T> header(String name, Object value) {
mrb.header(name, value);
rcb.header(name, value);
return this;
}
/**
* Adds the specified headers to every request.
*
* @param value
* The header values.
* <br>Can be <jk>null</jk> (existing values will be cleared).
* <br><jk>null</jk> null map values will be ignored.
* @return This object (for method chaining).
*/
public MockRemoteResource<T> headers(Map<String,Object> value) {
mrb.headers(value);
rcb.headers(value);
return this;
}
/**
* Adds an <c>Accept</c> header to every request.
*
* @param value The <c>Accept</c> header value.
* @return This object (for method chaining).
*/
public MockRemoteResource<T> accept(String value) {
mrb.accept(value);
rcb.accept(value);
return this;
}
/**
* Adds a <c>Content-Type</c> header to every request.
*
* @param value The <c>Content-Type</c> header value.
* @return This object (for method chaining).
*/
public MockRemoteResource<T> contentType(String value) {
mrb.contentType(value);
rcb.contentType(value);
return this;
}
/**
* Convenience method for setting <c>Accept</c> and <c>Content-Type</c> headers to <js>"application/json"</js>.
*
* @return This object (for method chaining).
*/
public MockRemoteResource<T> json() {
marshall(Json.DEFAULT);
return this;
}
/**
* Convenience method for setting <c>Accept</c> and <c>Content-Type</c> headers to <js>"application/json+simple"</js>.
*
* @return This object (for method chaining).
*/
public MockRemoteResource<T> simpleJson() {
marshall(SimpleJson.DEFAULT);
return this;
}
/**
* Convenience method for setting <c>Accept</c> and <c>Content-Type</c> headers to <js>"text/xml"</js>.
*
* @return This object (for method chaining).
*/
public MockRemoteResource<T> xml() {
marshall(Xml.DEFAULT);
return this;
}
/**
* Convenience method for setting <c>Accept</c> and <c>Content-Type</c> headers to <js>"text/html"</js>.
*
* @return This object (for method chaining).
*/
public MockRemoteResource<T> html() {
marshall(Html.DEFAULT);
return this;
}
/**
* Convenience method for setting <c>Accept</c> and <c>Content-Type</c> headers to <js>"text/plain"</js>.
*
* @return This object (for method chaining).
*/
public MockRemoteResource<T> plainText() {
marshall(PlainText.DEFAULT);
return this;
}
/**
* Convenience method for setting <c>Accept</c> and <c>Content-Type</c> headers to <js>"octal/msgpack"</js>.
*
* @return This object (for method chaining).
*/
public MockRemoteResource<T> msgpack() {
marshall(MsgPack.DEFAULT);
return this;
}
/**
* Convenience method for setting <c>Accept</c> and <c>Content-Type</c> headers to <js>"text/uon"</js>.
*
* @return This object (for method chaining).
*/
public MockRemoteResource<T> uon() {
marshall(Uon.DEFAULT);
return this;
}
/**
* Convenience method for setting <c>Accept</c> and <c>Content-Type</c> headers to <js>"application/x-www-form-urlencoded"</js>.
*
* @return This object (for method chaining).
*/
public MockRemoteResource<T> urlEnc() {
marshall(UrlEncoding.DEFAULT);
return this;
}
/**
* Convenience method for setting <c>Accept</c> and <c>Content-Type</c> headers to <js>"text/openapi"</js>.
*
* @return This object (for method chaining).
*/
public MockRemoteResource<T> openapi() {
marshall(OpenApi.DEFAULT);
return this;
}
/**
* Associates the specified {@link Marshall} with this client.
*
* <p>
* This is shorthand for calling <c>serializer(x)</c> and <c>parser(x)</c> using the inner
* serializer and parser of the marshall object.
*
* @param value
* The marshall to use for serializing and parsing HTTP bodies.
* <br>Can be <jk>null</jk> (will remote the existing serializer/parser).
* @return This object (for method chaining).
*/
public MockRemoteResource<T> marshall(Marshall value) {
if (value != null)
serializer(value.getSerializer()).parser(value.getParser());
else
serializer(null).parser(null);
return this;
}
/**
* Associates the specified {@link Serializer} with this client.
*
* @param value
* The serializer to use for serializing HTTP bodies.
* <br>Can be <jk>null</jk> (will remote the existing serializer).
* @return This object (for method chaining).
*/
public MockRemoteResource<T> serializer(Serializer value) {
rcb.serializer(value);
contentType(value == null ? null : value.getPrimaryMediaType().toString());
return this;
}
/**
* Associates the specified {@link Parser} with this client.
*
* @param value
* The parser to use for parsing HTTP bodies.
* <br>Can be <jk>null</jk> (will remote the existing parser).
* @return This object (for method chaining).
*/
public MockRemoteResource<T> parser(Parser value) {
rcb.parser(value);
accept(value == null ? null : value.getPrimaryMediaType().toString());
return this;
}
}