Swagger: #ApiModel annotation is ignored - jax-rs

I have a class which is annotated by #ApiModel. It contains a property which is an interface and the also with #ApiModel annotated implementation is located in another jar.
Example:
#ApiModel(description = "Container Object")
public class ContainerObject {
#ApiModelProperty(value = "generic field")
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type")
private CustomData customData;
}
Custom Data is an empty Pojo. Its implementation is as followed:
#ApiModel
public class CustomDataX extends CustomData {
#ApiModelProperty(value = "this is my super attribute")
private String myAttribute;
}
Now my Swagger Config is as followed:
#Startup
#ApplicationPath("/api")
public class JaxRsActivator extends Application {
public JaxRsActivator() {
super();
BeanConfig beanConfig = new BeanConfig();
beanConfig.setVersion("1.0.0");
beanConfig.setSchemes(new String[] { "http" });
beanConfig.setBasePath("/api");
beanConfig.setResourcePackage("ag.company.x");
beanConfig.setScan(true);
}
}
Sadly CustomDataX is not shown in the generated Swagger json. I'm using swagger-jaxrs version 1.5.22.
Do you know how to resolve this issue?
Edit: I already added a provided dependency to swagger-annotations as described in this stackoverflow post. Didn't work.

Related

java.lang.NullPointerException: null on AutoWiring a bean in StandAlone App

When trying to use #AutoWire feature with one of StandAlone Application unable to do so instead getting Null Pointer Exception. Please highlight my mistakes if any. Your help is appreciated.
Spring Ver 5.1.5.RELEASE and we're not using any xml config file to tell spring there are annotated classes to look into instead using #ComponentScan or #EnableAutoConfiguration at the top of AppConfig and boost strap the Context from main() class as a first line. But Autowiring works perfectly with internal bean/java classes of jdk(Environment) but not with custom POJO classes. If we're trying to get through getBean method then it works. But I'm trying to avoid creating context everywhere and using getBean() Please Refer below and help me only with your valuable guidelines.
public class ContextMaster {
private static AnnotationConfigApplicationContext appContext;
public static AnnotationConfigApplicationContext getApplicationContext() {
if (appContext == null) {
appContext = new AnnotationConfigApplicationContext(ContextConfig.class);
//appContext = new AnnotationConfigApplicationContext("com.xx.xx.xxx","xx.xxx.xxxx.xxx.datamanager");
logger.debug("Context Invoked !!");
}
return appContext;
}
}
#Configuration
#EnableAutoConfiguration
#PropertySource("classpath:db.properties")
#EnableTransactionManagement
#ComponentScans(value = {
#ComponentScan(basePackages = "xxxxx.datamanager"),
#ComponentScan(basePackages = "com.xx.xx.xxx"),
#ComponentScan(basePackages = "com.xx.xx.xxx.utils")})
public class AppConfig {
#Autowired
private Environment env;
#Bean
public DataSource getDataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(env.getProperty("db.driver"));
dataSource.setUrl(env.getProperty("db.url"));
return dataSource;
}
#Bean
public LocalSessionFactoryBean getSessionFactory() {
LocalSessionFactoryBean factoryBean = new LocalSessionFactoryBean();
//LocalSessionFactoryBean sessionFactoryBean = new AnnotationSessionFactoryBean();
factoryBean.setDataSource(getDataSource());
Properties props=new Properties();
props.put("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
props.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
props.put("hibernate.cache.region.factory_class", env.getProperty("hibernate.cache.region.factory_class"));
factoryBean.setHibernateProperties(props);
factoryBean.setAnnotatedClasses(xx.class, xxxx.class, xxxx.class, xxx.class);
return factoryBean;
}
#Bean
public HibernateTransactionManager getTransactionManager() {
return transactionManager;
}
}
// Here is NPE thrown when tried with auto-configured bean
#Component
public class Good extends Good11 {
#Autowired
private RxxxDyyyyHelper rdh;
//RxxxDyyyyHelper rdh =
ContextHelper.getApplicationContext().getBean(RxxxDyyyyHelper .class);
rdh.setProperty(); // NPE here
rdh.getProperty(); // NPE
}
// Here we're trying to initiate the LosUtils class
public class LosUtils {
public static void main(String args[]) throws Exception {
AnnotationConfigApplicationContext applicationContext = `ContextHelper.getApplicationContext();`
}
It seems like you didn't put the full code here, because your Good class won't compile this way..

How to use Jackson BeanDeserializerModifier?

I am trying to implement a custom deserializer.
Because I only want to add functionality to the default deserializer, I tried to store in my custom deserializer the default one: I would like to use the default to deserialize the json and then add other information.
I am trying to use BeanDeserializerModifier to register the custom deserializer.
SimpleModule module = new SimpleModule("ModelModule", Version.unknownVersion());
module.setDeserializerModifier(new BeanDeserializerModifier() {
#Override
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
JsonDeserializer<?> configuredDeserializer = super.modifyDeserializer(config, beanDesc, deserializer);
if (Document.class.isAssignableFrom(beanDesc.getBeanClass())) {
logger.debug("Returning custom deserializer for documents");
configuredDeserializer = new DocumentDeserializer(configuredDeserializer, (Class<Document>)beanDesc.getBeanClass());
}
return configuredDeserializer;
}
});
As you can see, if the object to generate is a "Document", I am modifying the deserializer returning a custom deserializer. I am passing the default deserializer to the constructor so I can use it later.
When I try to deserialize, Jackson fails with the error:
No _valueDeserializer assigned(..)
I have investigated and it seems that the default deserializer does not have the correct deserializers for its properties: for all the properties, it is using the deserializer FailingDeserializer that, of course, fails and returns the error mentioned above. This deserializer is supposed to be substituted but it is not.
It seems that, after calling the method modifyDeserializer, Jackson completes the configuration.
The custom deserializer that I am using is:
#SuppressWarnings("serial")
public class DocumentDeserializer extends StdDeserializer<Document> {
private JsonDeserializer<?> defaultDeserializer;
private DocumentDeserializer(JsonDeserializer<?> defaultDeserializer, Class<? extends Document> clazz) {
super(clazz);
this.defaultDeserializer = defaultDeserializer;
}
#Override
public Document deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
Document documentDeserialized = (Document) defaultDeserializer.deserialize(jp, ctxt);
/* I want to modify the documentDeserialized before returning it */
return documentDeserialized;
}
}
UPDATE:
I solved the problem using a different Deserializer:
public class CustomDeserializerModifier extends BeanDeserializerModifier {
private static final Logger logger = Logger.getLogger(CustomDeserializerModifier.class);
public CustomDeserializerModifier (Factory factory) {
this.factory = factory;
}
#Override
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
JsonDeserializer<?> configuredDeserializer;
if (CustomDeserializedNode.class.isAssignableFrom(beanDesc.getBeanClass())) {
Converter<Object, Object> conv = beanDesc.findDeserializationConverter();
JavaType delegateType = conv.getInputType(config.getTypeFactory());
configuredDeserializer = new CustomDeserializedNodeDeserializer(conv, delegateType, (JsonDeserializer<Document>) deserializer,
(Class<? extends CustomDocument<?>>)beanDesc.getBeanClass());
} else {
configuredDeserializer = super.modifyDeserializer(config, beanDesc, deserializer);
}
return configuredDeserializer;
}
#SuppressWarnings("serial")
public class CustomDeserializedNodeDeserializer extends StdDelegatingDeserializer<Object> {
private Class<? extends CustomDocument<?>> beanClass;
public CustomDeserializedNodeDeserializer(Converter<Object,Object> converter,
JavaType delegateType, JsonDeserializer<Document> delegateDeserializer, Class<? extends CustomDocument<?>> beanClass) {
super(converter, delegateType, delegateDeserializer);
this.beanClass = beanClass;
}
#Override
public CustomDeserializedNode deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
CustomDeserializedNode node = (CustomDeserializedNode)factory.createCustomDocument(beanClass);
CustomDeserializedNode documentDeserialized = (Document) super.deserialize(jp, ctxt, node);
return documentDeserialized;
}
}
}
Probably extending StdDelegatingDeserializer does what #StaxMan is suggesting.
This should be added in a FAQ, but what you need to do is to implement 2 interfaces:
ResolvableDeserializer (method resolve(...))
ContextualDeserializer (method createContextual(...))
and delegate these calls to defaultDeserializer in case it implements one or both interfaces. These are required for deserializer initialization; especially ContextualDeserializer through which property annotations are made available to deserializers.
And ResolvableDeserializer is used by BeanDeserializer to get deserializers for properties it has, if any; this is where _valueDeserializer in question is likely to be fetched.

Resteasy and Google Guice: how to use multiple #ApplicationPath and resource with #Injection?

I created a project to test the dependency injection offered by Google Guice in my Jax-rs resources, using Resteasy.
My intentions are:
Use multiple #ApplicationPath for the versions of my API. In each class annotated with #ApplicationPath I load a set of classes for the specific version.
Each resource have a #Inject (from Google Guice) in his constructor to inject some services.
I created two classes annotated with #ApplicationPath: ApplicationV1RS and ApplicationV2RS. In both I added the same resources classes (UserResource and HelloResource), only for my test.
My Module is configured like this:
public class HelloModule implements Module
{
public void configure(final Binder binder)
{
binder.bind(IGreeterService.class).to(GreeterService.class);
binder.bind(IUserService.class).to(UserService.class);
}
}
When I call http://localhost:9095/v1/hello/world or http://localhost:9095/v2/hello/world, I receive the same error:
java.lang.RuntimeException: RESTEASY003190: Could not find constructor
for class: org.jboss.resteasy.examples.guice.hello.HelloResource
Well, as I expected, this not works. The Google Guice is not "smart" to instantiate the resource classes using the construtor for me.
But I can't find a way to work. To be really honest, I'm really confuse about how the Google Guice, Jetty and Resteasy play with each other in this scenario.
If I abandon the idea of use #ApplicationPath, my resources work with Google Guice configuring my HelloModule like this:
public class HelloModule implements Module
{
public void configure(final Binder binder)
{
binder.bind(HelloResource.class);
binder.bind(IGreeterService.class).to(GreeterService.class);
binder.bind(UserResource.class);
binder.bind(IUserService.class).to(UserService.class);
}
}
But in this case, I'm passing the control to register my resources (HelloResource and UserResource) to Guice. It's not flexible for me, I can't setup my multiple #ApplicationPath.
So, what I'm missing or not understanding?
I created a project with the problemetic code. Is very easy to setup and test: https://github.com/dherik/resteasy-guice-hello/tree/so-question/README.md
Thanks!
When you have getClasses method in your Application then it tries to create instance for all the registered resources using the default constructor which is missing in our Resources class. One way is to create a default constructor and Inject the dependencies through setter Injection.
And then instead of overriding getClasses in ApplicationV1RS and ApplicationV2RS you override getSingletons. Since Resources can be Singleton.
Below are the changes that I made to make it work the way you want.
ApplicationV1RS.java
#ApplicationPath("v1")
public class ApplicationV1RS extends Application {
private Set<Object> singletons = new HashSet<Object>();
public ApplicationV1RS(#Context ServletContext servletContext) {
}
#Override
public Set<Object> getSingletons() {
Injector injector = Guice.createInjector(new HelloModule());
HelloResource helloResource = injector.getInstance(HelloResource.class);
UserResource userResource = injector.getInstance(UserResource.class);
singletons.add(helloResource);
singletons.add(userResource);
return singletons;
}
}
ApplicationV2RS.java
#ApplicationPath("v2")
public class ApplicationV2RS extends Application {
private Set<Object> singletons = new HashSet<Object>();
public ApplicationV2RS(#Context ServletContext servletContext) {
}
#Override
public Set<Object> getSingletons() {
Injector injector = Guice.createInjector(new HelloModule());
HelloResource helloResource = injector.getInstance(HelloResource.class);
UserResource userResource = injector.getInstance(UserResource.class);
singletons.add(helloResource);
singletons.add(userResource);
return singletons;
}
}
HelloResource.java
#Path("hello")
public class HelloResource {
#Inject
private IGreeterService greeter;
public HelloResource() {
}
#GET
#Path("{name}")
public String hello(#PathParam("name") final String name) {
return greeter.greet(name);
}
}
UserResource.java
#Path("user")
public class UserResource {
#Inject
private IUserService userService;
public UserResource() {
}
#GET
#Path("{name}")
public String hello(#PathParam("name") final String name) {
return userService.getUser(name);
}
}
Add #Singleton to your Service Classes.
Hope it helps.
I have also pushed the code to forked repo. check it out

Replace #Value property within #Configuration during Spring Boot test

Scenario
I've got a Spring Boot application with a #Configuration annotated Spring configuration class which contains some #Value annotated fields. For testing I want to replace these field values with custom test values.
Unfortunately these test values cannot be overridden using a simple properties file, (String) constants or similar, instead I must use some custom written property resolving Java class (e.g. TargetProperties.getProperty("some.username")).
The problem I have is that when I add a custom PropertySource to the ConfigurableEnvironment within my test configuration, it's already too late because this PropertySource will be added after the e.g. RestTemplate has been created.
Question
How can I override #Value annotated fields within a #Configuration class with properties obtained programmatically via custom Java code before anything else gets initialized?
Code
Production Configuration Class
#Configuration
public class SomeConfiguration {
#Value("${some.username}")
private String someUsername;
#Value("${some.password}")
private String somePassword;
#Bean
public RestTemplate someRestTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.getInterceptors().add(
new BasicAuthorizationInterceptor(someUsername, somePassword));
return restTemplate;
}
}
Test Configuration Class
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class SomeTest {
#SpringBootConfiguration
#Import({MySpringBootApp.class, SomeConfiguration.class})
static class TestConfiguration {
#Autowired
private ConfigurableEnvironment configurableEnvironment;
// This doesn't work:
#Bean
#Lazy(false)
// I also tried a #PostConstruct method
public TargetPropertiesPropertySource targetPropertiesPropertySource() {
TargetPropertiesPropertySource customPropertySource =
new TargetPropertiesPropertySource();
configurableEnvironment.getPropertySources().addFirst(customPropertySource);
return customPropertySource;
}
}
}
You can override properties directly in the #SpringBootTest annotation using the properties parameter:
#SpringBootTest(properties = {"some.username=user", "some.password=pwd"},
webEnvironment = SpringBootTest.WebEnvironment.NONE)
You can use #TestPropertySource
#TestPropertySource(
properties = {
"some.username=validate",
"some.password=false"
}
)
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class ApplicationTest {
//...
}
You can use constructor injection in production cases, which allows it to set the configuration manually:
#Configuration
public class SomeConfiguration {
private final String someUsername;
private final String somePassword;
#Autowired
public SomeConfiguration(#Value("${some.username}") String someUsername,
#Value("${some.password}") String somePassword) {
this.someUsername = someUsername;
this.somePassword = somePassword;
}
...
)
}
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class SomeTest {
private SomeConfiguration config;
#Before
public init() {
config = new SomeConfiguration("foo", "bar");
}
}

cannot use custom object mapper for single objects in jersey (works for collections)

I'm using the latest release of Jersey (2.13) on a glassfish server together with the latest version of jackson (version. 2.4) . I've written and registered a custom ObjectMapper, but it only collections seems to be serialized by my custom objectmapper.
I've seen a similar issue at this page: https://java.net/jira/browse/GLASSFISH-20815
but the workaround presented there did not work for me.
My jersey mapper provider class:
#Provider
public class JerseyMapperProvider implements ContextResolver {
private static ObjectMapper apiMapper = null;
public JerseyMapperProvider() {
}
#Override
public ObjectMapper getContext(Class<?> type) {
System.out.println(type.toString() + " this is only printed for collections...");
if (apiMapper == null) {
apiMapper = getDefaultObjectMapper();
}
return apiMapper;
}
...
okay, suddenly it worked by adding the following features to my resourceConfig...
#ApplicationPath("/")
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
super();
Map<String, Object> map = new HashMap<>();
map.put(CommonProperties.MOXY_JSON_FEATURE_DISABLE, true);
map.put(CommonProperties.JSON_PROCESSING_FEATURE_DISABLE, true);
addProperties(map);
....// more configuration here..
}
}