I'm running a SpringBoot (2.2.2.RELEASE) java application with springfox.boot.starter:3.0.0 and when I run it locally from within my Eclipse IDE it works well. The problem is that when I package it as Docker image and run the container then when I try to run http://localhost:8089/swagger-ui/# it displays the default Swagger Petstore example.
Here is my Docket #Bean:
#Configuration
public class SwaggerConfig {
#Bean
public Docket api() { //
return new Docket(DocumentationType.OAS_30) //
.apiInfo(DEFAULT_API_INFO) //
.select() //
.paths(PathSelectors.regex("/error").negate()) //
.build();
}
}
Obviously it's something related to running it from within a docker container, any idea what's missing to make it work?
Thank you!
EDIT:
Probably worth mentioning that I use to work with springfox:2.9.2 and it worked well both locally and as Docker container.
I think u must use http://localhost:8089/swagger-ui.html instead of http://localhost:8089/swagger-ui/#, this might work.
Related
In my Spring Boot project (v2.6), one of my components is using a Thymeleaf template engine to generate content.
I want to unit test my component, but I am struggling because it has a TemplateEngine as a constructor dependency :
public EmailNotifier(JavaMailSender emailSender,TemplateEngine templateEngine) {
this.emailSender = emailSender;
this.templateEngine=templateEngine;
}
I don't want to mock the TemplateEngine (the test would not have great value), I would prefer to use a "real" (and configured) templateEngine, and make sure that the content is generated as I expect. But I would like my test to be as "low-level" as possible, ie without loading the full application with Spring.
Spring Boot doesn't have a Thymeleaf "slice" like it has for Jpa or Web tests, but I guess I need something similar to that.
How can I get the minimum Spring magic in my test, so that it's both a realistic and fast test ?
This is what I ended up doing :
#SpringBootTest
class EmailNotifierTest {
//to have Spring Boot manage the thymeleaf config
#EnableAutoConfiguration
#Configuration
static class MinimalConfiguration {
#Autowired
TemplateEngine templateEngine;
#Bean
public EmailNotifier notifier(){
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost("localhost");
mailSender.setPort(3025);
return new EmailNotifier(mailSender, templateEngine);
}
}
#Autowired
EmailNotifier myEmailNotifier;
//tests using myEmailNotifier go here...
}
My object is ready to be used, with a templateEngine configured by Spring, the same way it will be when running in production. I guess we can exclude some auto configurations if needed, to go faster. But in my case I don't have too many other dependencies available, so the test is still quite fast to run, thanks to the minimal Spring overhead.
I'm running two spring boot applications on localhost. They are running on different ports. Let's call them SBA1 (Spring Boot App) and SBA2. There's an end point in SBA2 that I need to use. I already tested it directly on SBA2's swagger UI and I know its working. But when I try to use said end point from SBA1, I can't seem to call it. Here's what I tried so far,
This is the service that calls the class which calls the SBA2's endpoint:
#Service
public class HierarchyServiceImpl implements HierarchyService {
#Autowired
private PolicyRepository service;
//this is the class that calls SBA2's end point
#Autowired
private RuleEngineApi api;
#Override
public Policy calculateCollection(Collection collection) {
Policy policy = service.getPolicyData(collection.getPolicyNumber());
CollectionMapper mapper = new CollectionMapper();
Rule facts = new Rule();
facts.setFacts(mapper.mapCollections(collection, policy));
Rule rule = api.analyzeRules(facts);
return policy;
}
}
This is the class that calls SBA2's endpoint:
#FeignClient(name = "rule-engine-service", url = "http://localhost:8080")
public interface RuleEngineApi {
#PostMapping(value = "/v1/rule/analyzer", consumes = "application/json")
public Rule analyzeRules(Rule rule);
}
The problem with this approach is the application is unable to locate the bean for the RuleEngineApi class. It says this when I try to run SBA1:
required a bean of type '<path of class>.RuleEngineApi' that could not be found.\r\n\r\n\r\nAction:\r\n\r\nConsider defining a bean of type '<path of class>.RuleEngineApi' in your configuration.
I tried something like:
RuleEngineApi api = null;
Rule rule = api.analyzeRules(facts);
But of course that returned a NullPointerException. I would just like to emphasize that SBA2 is working fine. I'm just having trouble calling its endpoint when they (SBA1 and SBA2) are both running locally. Any help would be much appreciated
SBA1 is never creating an instance of RulesEngineApi. I suspect spring isn't scanning that package, but hard to tell without seeing package names and configs.
I've been trying to figure this out but it seems there was no problem at all. I've been attempting to run my application from Eclipse. Apparently when I run it from a terminal with the mvn spring-boot:run command, everything was working fine.
I haven't figure out what's wrong when I run it on Eclipse, but at least my application is running as expected
You need to configure #EnableFeignClient Annotation and you can also set the base package for where you're clients resides in. Some thing like this
#EnableFeignClients(basePackages = {"my.external.feign.client.package", "my.local.package"})
I am trying read ldap properties from a ldap-TEST.properties file
and trying to bind it to a java config class.for that i had specified
#PropertSource and defined a static Bean for propertysourcesplaceholderconfigurer.
still i am getting the Could not resolve placeholder spring.profiles.active in value classpath:/ldap-${spring.profiles.active}.properties below are project files please help me
#Configuration
#PropertySource("classpath:/ldap-${spring.profiles.active}.properties")
public class LdapConfig {
#Autowired
Environment env;
#Bean
public LdapContextSource contextSource() {
LdapContextSource contextSource = new LdapContextSource();
contextSource.setUrl(env.getRequiredProperty("ldap.url"));
contextSource.setBase(env.getRequiredProperty("ldap.base"));
contextSource.setUserDn(env.getRequiredProperty("ldap.userDn"));
contextSource.setPassword(env.getRequiredProperty("ldap.password"));
contextSource.afterPropertiesSet();
return contextSource;
}
#Bean
public LdapTemplate ldapTemplate() {
return new LdapTemplate(contextSource());
}
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
//ldap-TEST.properties file
ldap.base=dc=example,dc=com
ldap.password=password
ldap.port=839
ldap.userDn=cn=read-only-admin,dc=example,dc=com
ldap.url=ldap://ldap.forumsys.com:389
my main application
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
You can not use properties like ${spring.profiles.active} inside string value of Type annotation in spring. such properties would be injected into annotations like #Value which are for properties or methods.
The value behind spring.profiles.active is actually an array. So even if the value was correctly expanded, there would be corner cases when it wouldn't work the way you want.
It'd be nice if the paths configured via #PropertySource would work the same way the application.properties|yml does, but that is not the case at the moment (there is an active issue on GitHub about that). So alternatives have to be considered:
The simplest alternative would be to use the conventional files names application.properties|yml and application-{profile}.properties|yml. I do not see any good reason not to do it, but I do not know your project requirements so...
A bit more complicated, get the configured profiles using Java code, and configure the Spring environment programmatically. See this SO answer for more details.
If you are running on local machine, then Just change property file name application-default.property for temporary immediate work.
For permanent solution you have to check that your project is running on docker or not:
if running on docker then use below command:
mvn clean package -DskipTests=true && sudo docker build
sudo docker run -d -e spring.profiles.active="local" -e <other key and value of bootstrap.property file>
else if running on cloud server then follow below steps:
in bootstrap.property file put spring.profiles.active=local
rename application file to application-local.properties
you can also refer: 2.3.3. Profile Specific Files part at configuration document
I'm using spring boot + swagger 2 for documenting all the REST API .I'm able to list all the api of a controller when i have the below project structure.
If i move the swaggerconfig.java to the config package then i'm not able to list all api of a controller.i'm getting
This is my SwaggerConfig.java
#Configuration
#EnableAutoConfiguration
//#ComponentScan(basePackages="com.javainuse.swaggertest")
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket postsApi() {
return new Docket(DocumentationType.SWAGGER_2).groupName("public-api")
.apiInfo(apiInfo()).select().paths(postPaths()).build();
}
private Predicate<String> postPaths() {
return or(regex("/api/posts.*"), regex("/api/javainuse.*"));
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title("JavaInUse API")
.description("JavaInUse API reference for developers")
.termsOfServiceUrl("http://javainuse.com")
.contact("javainuse#gmail.com").license("JavaInUse License")
.licenseUrl("javainuse#gmail.com").version("1.0").build();
}
}
What i'm doing wrong
I fixed the problem. The problem is browser cache. I just cleared all the cache, and then I'm able to get the swagger-ui.html with all api list.
what worked for me is:
#Bean
public Docket customImplementation(){
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("your base package"))
.paths(PathSelectors.any())
.build();
notice paths is not specific, its any.
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select()
.apis(Predicates.not(RequestHandlerSelectors.basePackage("org.springframework.boot")))
.apis(Predicates.not(RequestHandlerSelectors.basePackage("org.springframework.cloud")))
.apis(Predicates.not(RequestHandlerSelectors.basePackage("org.springframework.security")))
.build();
}
Although the OP issue was solved already, I stumbled upon this thread and my solution was somewhat different. Here it is for others should they find themselves in the same position:
I had the same issue, but with using swagger-codegen-maven-plugin (v3)
The API is defined in a api.yml specification file and I generate the interface and models with the plugin.
It has an option to also generate a controller, in which case it was showing in the UI. However when I configure the controllers to be ignored/skipped for generation and just implement my own it was not showing.
The problem was the package. The plugin also auto generates the SwaggerDocumentationConfig class which includes all controllers from the API package that is configured. My own controllers had to be in the exact same package.
Code snippets to clarify:
In my pom.xml I have configured the plugin with:
<apiPackage>a.b.c.spec.api</apiPackage>
The auto generated SwaggerDocumentationConfig class contains:
#Bean
public Docket customImplementation() {
return (new Docket(DocumentationType.SWAGGER_2)).select()
.apis(RequestHandlerSelectors
.basePackage("a.b.c.spec.api"))
.build().apiInfo(this.apiInfo());
}
My controller lives in the package a.b.c.api so I had to change the apiPackage property to align and the issue was resolved.
This solved my issue.
Add .pathMapping("/") in the Docket
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.pathMapping("/");
I am working on maven based Java Spring Boot project.
In my case, it was a version mismatch of swagger and swagger-ui dependency in pom.xml
In my scenario, it's because of the #Profile("dev") configuration in SwaggerConfig limited for dev profile only, while I was running with another profile.
Checking versions of Swagger-UI and springfox-swagger2 in pox.xml
Adding #EnableSwagger2 in application.java helped.
I am working with Spring Integration with my project right now, specifically with MessageChannel/PublishSubscribeChannel. What I am trying to achieve is to create a broker module, so that other part of the system can call this module to send message to a specific MessageChannel.
Here is what I am doing now in the broker module:
#Configuration
public class BrokerConfiguration {
#Bean
public MessageChannel brokerChannel1() {
return new PublishSubscribeChannel();
}
}
and:
#Component
public class BrokerA {
#Autowired
#Qualifier("brokerChannel1")
public MessageChannel messageChannel;
public void sendAMessage() {
messageChannel.send(MessageBuilder.withPayload("This is a message!").build());
}
}
I have played around this setup by creating a SpringBootApplication within the broker module and it seems to work perfectly fine. However, when I try to use it in a different module of my system like this:
#Autowired
private BrokerA brokerA;
public void doSomethingHere() {
brokerA.sendAMessage();
}
I get a ClassCastException like this:
java.lang.ClassCastException: org.springframework.integration.channel.PublishSubscribeChannel cannot be cast to org.springframework.messaging.MessageChannel
And when I change messageChannel in BrokerA to the type of PublishSubscribeChannel, it will complain about PublishSubscribeChannel doesn't have a method called send().
This really baffles me. Any suggestions or comments? Thank you!
You have an old version of Spring Integration on the classpath; MessageChannel etc was moved from o.s.integration... to o.s.messaging in Spring 4.0.
You need to use Spring Integration 4.x.
Check your classpath, probably you have duplicated jars.
I ran your code on my environment with last spring boot version without any version specification about spring and it's working just right, the only error is on
MessageBuilder.withPayload("This is a message!") it should be MessageBuilder.withPayload("This is a message!").build()
And I verified using org.springframework.integration.support.MessageBuilder.
Try doing an explicit cast in the return statement of brokerChannel1() in BrokerConfiguration.