Have an openapi yml file with contains a simple get request with response, from this yml have the java files below been generated (among others).
How should these java files be used?
How to hook into the generated files?
Could simply copy the generated main and controller class into the main source tree, but does not seem to be the correct way.
--- edit ---
In the generated Controller class, how do I override the default responses that exist in the interface ExampleApi? Without having to modify the generated Controller class and have it in the VCS.
--- edit ---
build.gradle.kts
...
openApiGenerate {
generatorName.set("spring")
inputSpec.set("$rootDir/specs/api-example.yml")
outputDir.set("$buildDir/generated")
apiPackage.set("com.example.openapi.generated.api")
invokerPackage.set("com.example.openapi.generated.invoker")
modelPackage.set("com.example.openapi.generated.model")
configOptions.set(mapOf(
"dateLibrary" to "java8"
))
systemProperties.set(mapOf(
"modelDocs" to "false"
))
}
...
What should be done with these classes?
package com.example.openapi.generated.invoker;
import com.fasterxml.jackson.databind.Module;
import org.openapitools.jackson.nullable.JsonNullableModule;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.ExitCodeGenerator;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#SpringBootApplication
#ComponentScan(basePackages = {"com.example.openapi.generated.invoker", "com.example.openapi.generated.api" , "org.openapitools.configuration"})
public class OpenAPI2SpringBoot implements CommandLineRunner {
#Override
public void run(String... arg0) throws Exception {
if (arg0.length > 0 && arg0[0].equals("exitcode")) {
throw new ExitException();
}
}
public static void main(String[] args) throws Exception {
new SpringApplication(OpenAPI2SpringBoot.class).run(args);
}
static class ExitException extends RuntimeException implements ExitCodeGenerator {
private static final long serialVersionUID = 1L;
#Override
public int getExitCode() {
return 10;
}
}
#Bean
public WebMvcConfigurer webConfigurer() {
return new WebMvcConfigurer() {
/*#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("*")
.allowedHeaders("Content-Type");
}*/
};
}
#Bean
public Module jsonNullableModule() {
return new JsonNullableModule();
}
}
package com.example.openapi.generated.api;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.NativeWebRequest;
import java.util.Optional;
#Controller
#RequestMapping("${openapi.basic.base-path:}")
public class ExampleApiController implements ExampleApi {
private final NativeWebRequest request;
#org.springframework.beans.factory.annotation.Autowired
public ExampleApiController(NativeWebRequest request) {
this.request = request;
}
#Override
public Optional<NativeWebRequest> getRequest() {
return Optional.ofNullable(request);
}
}
You need to tell gradle to compile the files, there is no need to copy them.
Add the path with the generated files to the sourceSets of your project. Something like this:
sourceSets {
main {
java {
srcDir("$buildDir/generated")
}
}
}
public class ServiceLoaderDemo {
public CPService demo() {
ServiceLoader<CPService> serviceLoader = ServiceLoader.load(CPService.class);
return iterate(serviceLoader);
}
public CPService iterate(ServiceLoader<CPService> serviceLoader) {
CPService nonDefault = null;
for (CPService cpService : serviceLoader) {
cpService.show();
if(!cpService.isDefault())
{
nonDefault = cpService;
}
}
return nonDefault;
}
}
I need to write unit tests for iterate method with following cases:
a default service and a non-default service
a default service only
a non-default service only
more than one non-default service
I tried to mock ServiceLoader class as follows:
import static org.fest.reflect.core.Reflection.method;
import static org.powermock.api.mockito.PowerMockito.mock;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ServiceLoader;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import junit.framework.TestCase;
#RunWith(PowerMockRunner.class)
#PrepareForTest(ServiceLoader.class)
public class ServiceLoaderDemoTest extends TestCase {
private ServiceLoaderDemo serviceLoaderDemo = new ServiceLoaderDemo();
public void testIterate() {
final ServiceLoader mockServiceLoader = mock(ServiceLoader.class);
IteratorDummy iterator = new IteratorDummy();
iterator.cpServices.add(new CPServiceImplTwo());
iterator.cpServices.add(new CPServiceImplOne());
PowerMockito.when(mockServiceLoader.iterator()).thenReturn(iterator);
CPService methodToTest = null;
try {
methodToTest = method("iterate").withReturnType(CPService.class).withParameterTypes(ServiceLoader.class).in(serviceLoaderDemo).invoke(mockServiceLoader);
} catch (SecurityException e) {
e.printStackTrace();
}
assertEquals(methodToTest.getClass(), ServiceLoaderDemoTest.class);
}
}
class IteratorDummy implements Iterator<CPService> {
public List<CPService> cpServices = new ArrayList<>();
#Override
public boolean hasNext() {
return cpServices.iterator().hasNext();
}
#Override
public CPService next() {
CPService service = cpServices.iterator().next();
return service;
}
}
This is throwing NullPointerException. I am unable to write unit tests for this. please help me.
I'm trying to intercept requests to my jaxrs apis basead on annotations, my filter is simple:
#Provider
public class Filter implements ContainerRequestFilter {
#Context
private ResourceInfo info;
#Override
public void filter(ContainerRequestContext crc) throws IOException {
// here I'm trying to get the annotate resource class or method.
info.getResourceClass().isAnnotationPresent(MyCustomAnnotation.class);
}
}
this works fine with a simple resource like this: (works both in class and method)
#Path("/")
public class SimpleResource {
#GET
#MyCustomAnnotation
public String test() {
return "test";
}
}
But in my real application, I have scenarios like this:
#Path("/")
public class RootResource {
#Inject
ChildResource childResource;
#Path("child")
public ChildResource child () {
return childResource;
}
}
So, I wanna put my custom annotation only on ResourceLocator and on the fly verify that the final resource contains the annotation.
#Path("/")
#CustomAnnotation
public class RootResource {
#Inject
ChildResource childResource;
#Path("child")
public ChildResource child () {
return childResource;
}
}
is it possible? or i can only get information about the matched resource?
"In jersey how would be this?"
With Jersey you have access to the resource model, and ways to traverse the model. You can see jersey server introspectionmodeller not public in v2.0? for some explanation and examples of how to traverse the model and Resource and ResourceMethod. Other than that, there is not much documentation these APIs.
Below is a complete example Using Jersey Test Framework. You can run the class like any other JUnit test. You just need this one dependency to run it
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>2.19</version>
<scope>test</scope>
</dependency>
And here's the test.
import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.List;
import java.util.logging.Logger;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
import static junit.framework.Assert.assertEquals;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.filter.LoggingFilter;
import org.glassfish.jersey.server.ExtendedUriInfo;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.model.Resource;
import org.glassfish.jersey.server.model.ResourceMethod;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
public class ResourceModelTest extends JerseyTest {
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public static #interface ResourceAnnotation {
String value();
}
#Path("root")
#ResourceAnnotation("SomeValue")
public static class ParentResource {
#Path("sub")
public ChildResource getChild() {
return new ChildResource();
}
#GET
public String get() {
return "ROOT";
}
}
public static class ChildResource {
#GET
public String get() {
return "CHILD";
}
}
#Provider
public static class ResourceFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext requestContext,
ContainerResponseContext responseContext) throws IOException {
ExtendedUriInfo info = (ExtendedUriInfo) requestContext.getUriInfo();
List<ResourceMethod> resourceLocators = info.getMatchedResourceLocators();
if (!resourceLocators.isEmpty()) {
Resource parent = resourceLocators.get(0).getParent();
Class<?> parentClass = parent.getHandlerClasses().iterator().next();
ResourceAnnotation anno = parentClass.getAnnotation(ResourceAnnotation.class);
if (anno != null) {
responseContext.getHeaders().putSingle("X-SubResource-Header", anno.value());
}
}
}
}
#Override
public ResourceConfig configure() {
return new ResourceConfig(ParentResource.class)
.register(ResourceFilter.class);
}
#Override
public void configureClient(ClientConfig config) {
config.register(new LoggingFilter(Logger.getAnonymousLogger(), true));
}
#Test
public void get_child_resource() {
Response response = target("root/sub").request().get();
assertEquals(200, response.getStatus());
assertEquals("SomeValue", response.getHeaderString("X-SubResource-Header"));
}
}
I have a bean annoteded with JSR 303 annotations. I also added Spring aspect (#Around) for handling MethodConstraintViolationException. My problem is: if I execute methods with correct parameters - my aspect works (is executed - breakpoints added), but when I run methods with incorrect parameters then MethodConstraintViolationException is thrown and my aspect is not executed.
package noname.exception;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.hibernate.validator.method.MethodConstraintViolationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import noname.service.exceptions.ValidationException;
import noname.utils.ValidationExceptionProcessor;
#Aspect
public class ExceptionAspect implements Ordered {
#Autowired
private ValidationExceptionProcessor processor;
#Pointcut(value = "execution(* noname.conversionstrategy.api.IDocumentConverter.*(..))")
public void aopDocumentConverterPointcut() {
}
#Pointcut(value = "execution(* noname.service.api.IMailMerger.*(..))")
public void aopMailMargeServicePointcut() {
}
#SuppressWarnings("deprecation")
#Around("aopDocumentConverterPointcut() || aopMailMargeServicePointcut()")
public Object exceptionsAspect(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
try {
return proceedingJoinPoint.proceed();
} catch ( Throwable e ) {
if (e instanceof MethodConstraintViolationException) {
ValidationException exp = processor.process((MethodConstraintViolationException) e);
throw exp;
} else {
throw e;
}
}
}
#Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
Intefaces (IMailMerger and IDocumentConverter) are similar:
package noname.conversionstrategy.api;
import java.util.List;
import javax.validation.constraints.NotNull;
import org.springframework.validation.annotation.Validated;
import noname.service.domain.DocumentActionInput;
import noname.service.domain.DocumentActionResult;
import noname.validator.ValidActionInput;
#Validated
public interface IDocumentConverter {
DocumentActionResult convertDocument(#NotNull(message = "DocumentActionInput must be provided") #ValidActionInput DocumentActionInput document);
List<DocumentActionResult> convertDocuments(#NotNull(message = "DocumentActionInput must be provided") #ValidActionInput List<DocumentActionInput> documents);
}
I suppose spring execute first bean validation (it is probably executed with aspect too (?) ). If this validation throws MethodConstraintViolationException then my aspect is not executed, because spring aop doesn't support catching exceptions from anoother aspect (need confirmation).
I also created test with proxy (everything with my aspect looks fine):
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:applicationContextTest.xml"})
public class ExceptionAspectSpringTest {
#Autowired
private IDocumentConverter documentConverter;
#Autowired
private ExceptionAspect exceptionAspect;
private IDocumentConverter proxy;
#Before
public void setUp() {
AspectJProxyFactory aspectJProxyFactory = new AspectJProxyFactory(documentConverter);
aspectJProxyFactory.addInterface(IDocumentConverter.class);
aspectJProxyFactory.addAspect(exceptionAspect);
proxy = aspectJProxyFactory.getProxy();
}
#Test( expected = ValidationException.class )
public void shouldThownValidationException() {
DocumentActionInput document = new DocumentActionInput();
proxy.convertDocument(document);
}
}
Any help appreciated
I'm beginning with jersey and trying to get freemarker working with it using TDD. I want to make a ViewProcessor for my templates, but fail to inject the servlet context in the class.
Here is the class code :
#Provider
public class myProcessor implements ViewProcessor<Template> {
[...]
#Context
public ServletContext myContext;
[...]
freemarkerConfiguration.setTemplateLoader(
new WebappTemplateLoader(myContext,
myContext.getInitParameter("freemarker.template.path")));
[...]
}
And here is the test code :
public class myProcessorTest extends JerseyTest {
public static myProcessor mp;
public myProcessorTest() throws Exception{
super(new WebAppDescriptor.Builder("com.domain").build());
}
#Test
public void firstTest(){
mp = new myProcessor();
String path = new String("test.ftl");
Template template = mp.resolve(path);
assertNotNull(template);
}
}
I use maven with dependencies as follow:
<dependency>
<groupId>com.sun.jersey.jersey-test-framework</groupId>
<artifactId>jersey-test-framework-grizzly</artifactId>
<version>1.5-SNAPSHOT</version>
<scope>test</scope>
</dependency>
My code runs fine when I deploy to my local jetty server. But if I want to test the code in my IDE, it failed to inject the servlet context (#Context) : myContext is null when I run the test :/
I think I'm missing something, but I'm a complete beginner with servlet world.
Here's a technique for testing a specific resource class, using Jersey Test Framework, with servlet support. Also demonstrates how to customize the ServletContext.
import javax.servlet.ServletContext;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.servlet.ServletContainer;
import org.glassfish.jersey.test.DeploymentContext;
import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.test.ServletDeploymentContext;
import org.glassfish.jersey.test.TestProperties;
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
import org.glassfish.jersey.test.spi.TestContainerException;
import org.glassfish.jersey.test.spi.TestContainerFactory;
import static org.mockito.Mockito.mock;
/**
* A base class for testing web resources.
*/
public abstract class WebResourceTest extends JerseyTest {
/**
* Creates a JAX-RS resource configuration for test purposes.
*/
#Override
protected abstract ResourceConfig configure();
/**
* Creates a test container factory with servlet support.
*/
#Override
protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
return new GrizzlyWebTestContainerFactory();
}
/**
* Configures a deployment context for JAX-RS.
*/
#Override
protected DeploymentContext configureDeployment() {
ResourceConfig app = configure();
app.register(new Feature() {
#Context
ServletContext servletContext;
#Override
public boolean configure(FeatureContext context) {
servletContext.setAttribute("example", new Object());
return true;
}
});
return ServletDeploymentContext.forServlet(new ServletContainer(app)).build();
}
}
A usage example:
import org.glassfish.jersey.server.ResourceConfig;
import javax.ws.rs.core.Context;
import javax.ws.rs.Path;
import javax.ws.rs.GET;
import javax.ws.rs.core.Response;
import static org.mockito.Mockito.spy;
import static org.testng.Assert.assertEquals;
import static org.junit.Assert.*;
public class MyResourceTest extends WebResourceTest {
private MyResource resource;
#Override
protected ResourceConfig configure() {
resource = spy(new MyResource());
return new ResourceConfig().register(resource);
}
#Test
public void testSomething() {
Response r = target("/myresource").request().get();
assertEquals(200, r.getStatus());
assertEquals(1, resource.count);
}
}
#Path("/myresource")
public class MyResource {
int count = 0;
#Context
protected ServletContext servletContext;
#GET
public void get() {
Object attr = servletContext.getAttribute("example");
count++;
}
}
There's a couple of ways to do it. Remove the constructor and implement a configure() method like this:
public class myProcessorTest extends JerseyTest {
public static myProcessor mp;
#Override
protected AppDescriptor configure() {
return new WebAppDescriptor.Builder("com.domain")
.contextParam("contextConfigLocation", "classpath:/applicationContext.xml")
.contextPath("/").servletClass(SpringServlet.class)
.contextListenerClass(ContextLoaderListener.class)
.requestListenerClass(RequestContextListener.class)
.build();
}
or alternatively you can annotate your test with the spring context:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath:applicationContext.xml")
public class MyProcessorTest extends JerseyTest {
public static myProcessor mp;
There is a solution to this problem that does not require spring, assuming you are using the default/standard Grizzy2 test framework provider. According to this answer the jersey-test-framework-provider-grizzly2 framework provider does not utilize a servlet environment in constructing the application context. Your symptoms result from there being no ServletContext instance to inject.
The workaround is to provide the test container for the unit tests yourself. First, modify your dependencies:
<!--<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>2.25</version>
<scope>test</scope>
</dependency>-->
<dependency>
<groupId>org.glassfish.jersey.test-framework</groupId>
<artifactId>jersey-test-framework-core</artifactId>
<version>2.25</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-grizzly2-servlet</artifactId>
<version>2.25</version>
</dependency>
Then, modify your test to provide a Grizzy servlet container:
#Override
protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
return (final URI baseUri, final DeploymentContext deploymentContext) ->
new TestContainer() {
private HttpServer server = null;
#Override
public ClientConfig getClientConfig() {
return null;
}
#Override
public URI getBaseUri() {
return baseUri;
}
#Override
public void start() {
try {
this.server = GrizzlyWebContainerFactory.create(baseUri, Collections
.singletonMap("jersey.config.server.provider.packages", "<your-package-name>"));
} catch (final ProcessingException | IOException cause) {
throw new TestContainerException(cause);
}
}
#Override
public void stop() {
this.server.shutdownNow();
}
};
}
I assume that you are going to use this in multiple unit tests, so it may be wise to extend JerseyTest so this common configuration may be performed automatically. Additionally, it may be worth reviewing org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory to see if there is any functionality provided by the test container that you wish to emulate/preserve. The example provided should be able to be dropped into your test to at least confirm this is a fix.
EDIT: In my own implementation, I required the ability to still supply a ResourceConfig when generating the server. I suspect that this is likely to be the common case for other Jersey Test Framework users. A working example of the proposed TestContainerFactory follows.
import java.io.IOException;
import java.net.URI;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletContext;
import javax.ws.rs.ProcessingException;
import javax.ws.rs.core.UriBuilder;
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.grizzly.servlet.WebappContext;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.test.DeploymentContext;
import org.glassfish.jersey.test.spi.TestContainer;
import org.glassfish.jersey.test.spi.TestContainerException;
import org.glassfish.jersey.test.spi.TestContainerFactory;
import org.glassfish.jersey.test.spi.TestHelper;
public class RestTestContainerFactory implements TestContainerFactory {
public static class RestTestContainer implements TestContainer {
private static final Logger LOGGER = Logger.getLogger(RestTestContainer.class.getName());
private URI baseUri = null;
private final HttpServer server;
public RestTestContainer(final URI baseUri, final DeploymentContext context) {
this.baseUri = UriBuilder.fromUri(baseUri).path(context.getContextPath()).build();
if(LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("Creating RestRestContainer configured at the base URI "+TestHelper.zeroPortToAvailablePort(baseUri));
}
try {
final WebappContext webContext = new WebappContext("TestContext", context.getContextPath());
context.getResourceConfig()
.register(new AbstractBinder() {
#Override
protected void configure() {
bind(webContext).to(ServletContext.class);
}
});
this.server = GrizzlyHttpServerFactory.createHttpServer(this.baseUri, context.getResourceConfig(), false);
webContext.deploy(this.server);
} catch (final ProcessingException cause) {
throw new TestContainerException(cause);
}
}
#Override
public ClientConfig getClientConfig() {
return null;
}
#Override
public URI getBaseUri() {
return baseUri;
}
#Override
public void start() {
if(server.isStarted()) {
LOGGER.warning("Ignoring start request - RestTestContainer is already started");
} else {
LOGGER.fine("Starting RestTestContainer...");
try {
server.start();
if(baseUri.getPort() == 0) {
baseUri = UriBuilder.fromUri(baseUri)
.port(server.getListener("grizzly").getPort())
.build();
LOGGER.info("Started GrizzlyTestContainer at the base URI "+baseUri);
}
}
catch(final ProcessingException | IOException cause) {
throw new TestContainerException(cause);
}
}
}
#Override
public void stop() {
if(server.isStarted()) {
LOGGER.fine("Stopping RestTestContainer...");
server.shutdownNow();
} else {
LOGGER.warning("Ignoring stop request - RestTestContainer is already stopped");
}
}
}
#Override
public TestContainer create(final URI baseUri, final DeploymentContext context) {
return new RestTestContainer(baseUri,context);
}
}
Frustratingly, grizzly's GrizzlyWebContainerFactory will provide a servlet context, but not configure with a resource config. Inversely, GrizzlyHttpServerFactory will configure an application with a ResourceConfig, but will not provide a web context.
We can work around this by creating the WebappContext (extends ServletContext) manually, configuring it, and then injecting it into the resource config by means of an AbstractBinder.